动态加载dex(Part2)
上周五说到了把dex文件加载到DexClassLoader中去;加载完成后怎么调用dex中的源码呢?
0x00 Robile中的实现
在Robile框架的PluginActivity.java中是这样实现的:
pluginContextClass = mPluginPackage + ".Plugin";
所有的插件都符合这样的规范,package根目录下有个.Plugin.java作为插件入口。
具体执行哪一个方法?Plugin.java是继承Activityable的,PluginActivity的onCreate结束之后执行onPluginCreate,onPluginCreate中执行了mActivity.onCreate():
protected void onPluginCreate(Bundle savedInstanceState) {
if (mActivity != null && mActivity instanceof Activityable) {
mActivity.onCreate(this, savedInstanceState);
}
}
这样就进入了插件的生命周期。问题是,这里的mActivity是怎么映射到Plugin的生命周期的?我们可以看到:
Constructor<?> pluginMainConstructor = pluginMain.getConstructor();//获取pluginMain的构造方法(pluginMain就是读取的Plugin)
mActivity = (Activityable) pluginMainConstructor
.newInstance();
这里初始化了一个Plugin.java这个类的构造器,然后mActivity获得了它的instance。这就是为什么mActivity的onCreate、onResume等等生命周期对应了Plugin的生命周期。
0x01 执行具体函数
如果是「热修复」呢,也就是想要执行dex中某个具体的方法,可以反射到具体的方法然后执行:
targetClass = dexClassLoader.loadClass("com.drunkpiano.TestClass");
// 遍历类中所有的方法
Method[] methods = targetClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Log.e(TAG, methods[i].toString());
}
Method method = targetClass.getDeclaredMethod("function");// 读取指定的方法
method.setAccessible(true);//把方法设为public以支持外部调用
String string = (String) method.invoke(targetClass.newInstance());// 调用方法
Toast.makeText(this, string, Toast.LENGTH_LONG).show();
另外,看到有些方案说,还可以在主工程中打包一些接口来实现。这里不深究了。
-NOV 28