给插件提供Serivce(二周目)
昨天讲到怎么模仿PluginActivity给插件提供一个PluginService,是一个大概思路,今天写的时候有些细节记录在这里。
获取Service的构造器
首先,要想获得PluginService的构造器,就需要:
- 加载一个文件路径+包名+PluginService的类文件(木偶Service)
- 要满足1,就要先把外部的dex/apk加载到指定的优化路径(optimizedDirectory)里面。
PluginActivity中具体是这样操作的:
- new一个BaseDexClassLoader(),参数是:
super(apkPath, optimizedDirectory, libraryPath, parent);
这样一来我们就有了一个指定了optimizedDirectory的classLoader。
用这个classLoader的loadClass(className)方法加载指定的类。由于这个classLoader已经绑定了optimizedDirectory,所以className是包名+类名就可以了。
用2中得到的serviceClass这个获得构造器。
Constructor<?> serviceConstructor = serviceClass
.getConstructor();
问题来了,在PluginService需要这样的步骤吗?
答案是我们需要获得刚才new出来的那个classLoader,然后去直接用它去加载插件中继承Serviceable的那个类。
怎么获取刚才的classLoader呢?事实上,在刚才构造classLoader的时候,PluginClassLoader用Map保存了classLoader的弱引用:
//PluginClassLoader.java
ClassLoader pluginLoader = new PluginClassLoader(apkPath,
apkOutputDir, libPath, parent);
if (pluginLoader != null) {
loader = pluginLoader;
pluginLoaders.put(apkPath, new WeakReference<ClassLoader>(
loader));
}
这样的话目测就可以了,但总觉得还少点什么。
其他
另外,对于多Service的支持,我们知道Activity是可以启动其他Activity的,也可以启动本身的Activity,并且是standard启动模式,可以覆盖(有个疑问,目前插件中不是仅用到一个Activity实例吗。。);但是Service不能复用(为什么?是不是因为再次new 同一个Service木偶的时候,ClassLoader发现parent已经加载过这个类了,所以返回了同样的实例?)。明天看看。
-DEC 1ST
Appendix:
现在的PluginService.java:
public class PluginService extends Service {
/**
* 插件文件路径,文件路径+包名+PluginService.java信息加起来才能构造Plugin的Service实例
*/
private String mPluginPath;
/**
* 插件包名
*/
private String mPluginPackage;
/**
* 获取PluginActivity初始化的classLoader
*/
private ClassLoader mClassLoader;
/**
* Serviceable实例
*/
protected Serviceable mPluginService;
private void useClassLoaderToLoadService(String mPluginPath, String packageNameAndServicePath) {
//mPluginPath是外部apk路径,optimize之前的路径;这里是用key取map中的值的操作
mClassLoader = PluginClassLoader.getClassLoader(mPluginPath);
try {
Class<?> serviceClass;
// if (CJConfig.DEF_STR.equals(mDexPath)) {
// serviceClass = super.getClassLoader().loadClass(mClass);
// } else {
// serviceClass = this.getClassLoader().loadClass(mClass);
// }
//packageNameAndServicePath是包名+木偶类名,用来定位插件中的木偶位置
serviceClass = mClassLoader.loadClass(packageNameAndServicePath);
Constructor<?> serviceConstructor = serviceClass
.getConstructor();
mPluginService = (Serviceable) serviceConstructor.newInstance();
} catch (Exception e) {
}
// mPluginService.setProxy(this, mDexPath);
}
private void initPath() {
//获取插件路径。mPluginPath这个路径是要拿去loadPlugin的(构造BaseDexClassLoader),所以,mPluginPath是指下载的apk的路径或是assets中内置apk的路径
mPluginPath = PluginManager.getInstance().getPlugin();
mPluginPackage = getPluginPackageName(mPluginPath);
}
private String getPluginPackageName(String pluginPath) {
try {
PackageInfo packageInfo = getPackageManager()
.getPackageArchiveInfo(pluginPath, 0);
if (packageInfo == null) {
return null;
}
return packageInfo.packageName;
} catch (Exception e) {
return null;
}
}
@Override
public void onCreate() {
initPath();
useClassLoaderToLoadService(mPluginPath, mPluginPackage + ".RemoteService");
if (mPluginService != null) {
mPluginService.onCreate();
}
super.onCreate();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (mPluginService != null) {
mPluginService.onConfigurationChanged(newConfig);
}
super.onConfigurationChanged(newConfig);
}
@Override
public void onLowMemory() {
if (mPluginService != null) {
mPluginService.onLowMemory();
}
super.onLowMemory();
}
@Override
@SuppressLint("NewApi")
public void onTrimMemory(int level) {
if (mPluginService != null) {
mPluginService.onTrimMemory(level);
}
super.onTrimMemory(level);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
if (mPluginService != null) {
mPluginService.onUnbind(intent);
}
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
if (mPluginService != null) {
mPluginService.onRebind(intent);
}
super.onRebind(intent);
}
}