插件化开发的第三种方法,其实是在第二种Hook式方法上继续改进,因为第二种hook式存在一个问题,那就是第三步中将多个apk插件进行加载,导致dexElements会很大,占用内存比较大,很有可能当插件很多时候,直接oom,因此产生了一种新的方法,就是讲LoadApk进行扩展,当加载插件时候,使用插件的LoadApk,这样就不会产生占很大内存的dexElements了。
下面是步骤:
1、绕过AMS检测,使用ProxyActivity替换PluginActivity
2、将ProxyActivity换回PluginActivity
3、将两个apk文件一起加载
1、使用ProxyActivity替换PluginActivity
/**
* 要在AMS执行之前,将插件中的Activity替换成ProxyActivity,替换成在宿主app的Manifest中注册的activity
*
* @throws Exception
*/
private void hookAMSAction() throws Exception {
// 我们要拿到IActivityManager对象,才能让动态代理里面的 invoke 正常执行下
// 执行此方法 static public IActivityManager getDefault(),就能拿到 IActivityManager
Class<?> mActivityManagerNativeClass = Class.forName("android.app.ActivityManager");
final Object mIActivityManager = mActivityManagerNativeClass.getMethod("getService").invoke(null);
Field mActivityManagerClassSingletonField = mActivityManagerNativeClass.getDeclaredField("IActivityManagerSingleton");
mActivityManagerClassSingletonField.setAccessible(true);
Object mActivityManagerClassSingleton = mActivityManagerClassSingletonField.get(null);
Class mIActivityManagerClass = Class.forName("android.app.IActivityManager");
Object mActivityManagerProxy = Proxy.newProxyInstance(HookApplication.class.getClassLoader(),
new Class[]{
mIActivityManagerClass}, //监听的接口
new InvocationHandler() {
//监听的回调
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
//执行自己的逻辑,就是将插件的activity替换成宿主的ProxyActivity
Intent intent = new Intent(HookApplication.this, Proxy2Activity.class);
intent.putExtra("actionIntent", (Intent) args[2]);
args[2] = intent;
}
Log.e("hookapplication", "拦截到了IActivityManager里面的方法" + method.getName());
//要保证方法继续执行下去
return method.invoke(mIActivityManager, args);
}
});
if (mActivityManagerClassSingleton == null || mActivityManagerProxy == null) {
throw new IllegalArgumentException("系统不匹配,请检查兼容性");
}
Class<?> mSingleTonClass = Class.forName("android.util.Singleton");
Field mInstance = mSingleTonClass.getDeclaredField("mInstance");
mInstance.setAccessible(true); //授权
mInstance.set(mActivityManagerClassSingleton, mActivityManagerProxy); //替换原来的值
}
2、将ProxyActivity换回PluginActivity
/**
* 在activity创建时候,将ProxyActivity替换成原来跳转的activity
*
* @throws Exception
*/
private void hookLaunchActivity() throws Exception {
Class<?> mActivityThreadClass = Class.forName("android.app.ActivityThread");
Object mActivityThread = mActivityThreadClass.getMethod("currentActivityThread").invoke(null);
Field mHField = mActivityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Object mH = mHField.get(mActivityThread);
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
//将系统中的Handler.Callback实现,替换成我们自己写的CustomCallback
mCallbackField.set