写在开头:
上一篇插件化之旅3文章中提到了融合宿主和插件两个dex文件的方式来加载class,但是遗留了两个问题:1.多个插件同时加载导致dexElement过大,会造成内存爆棚问题;2.如果插件发生崩溃,那么同样会导致宿主崩溃,耦合性太高了。这一篇文章主要讲怎么去解决这两个问题。
一、LoadApk是什么?
文章开始之前,回想前面文章用到的Hook技术,通过设置接口方式将ActivityThread类的内部类Handler的handlerMessage方法捕获到,然后得到msg.obj这个对象,查看源码可知,这个obj其实是ActivityClientRecord,ActivityClientRecord是ActivityThread的静态内部类,它有一个成员对象是LoadApk。
LoadApk是apk文件在内存中的表现形式。就跟我们以前说过的dex文件在内存中的表现形式是Element一样。
二、源码中怎么生成LoadApk对象?
故事的开始要从LAUNCH_ACTIVITY说起...
image.png
image.png
ActivityThread这个类有个成员变量:ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<String, WeakReference<LoadedApk>>();
这个mPackages是包名和LoadApk组合的键值对,Google工程师应该是有意而为之,给我们的插件化留了个后门。看到这里思路就产生了:将插件的包名和loadApk组合成键值对,再put到这个mPackages集合里面,这样不就形成了解耦性了吗?
2.1问题一:如何将插件的apk转换为LoadApk对象?
源码中可以知道,生成LoadApk对象的方法有两个,一是(public)getPackageInfoNoCheck,另一个是(private)getPackageInfo,那反射哪个方法好些呢?
其实Google开发工程师在编写Android源代码时,有个基本的原则,就是public方法不会去修改,而private方法常常是会修改的,所以,我们反射的时候,也尽量去反射那些public的方法。
好了,决定要反射getPackageInfoNoCheck方法,需要知道参数类型,以及调用invoke时,需要传入参数对象,getPackageInfoNoCheck里面有两个参数,类型分别是:1.ApplicationInfo;2.CompatibilityInfo。同样也是靠反射来实现:
1.获取ApplicationInfo(就是我们在AndroidManifest.xml中注册的信息),在广播插件的文章中,我们已经反射生成了ApplicationInfo,所以现在就直接拿来使用:
image.png
2.获取CompatibilityInfo,得到LoadApk后要做两件事,一是替换LoadApk内部的成员变量ClassLoader;二是将插件的包名和loadApk作为键值对插入到mPackages;
image.png
三、为什么还要Hook PMS的getPackageInfo方法?
前面两步我们已经能够将插件的class加入到宿主中,也能实现完美的解耦,但是只是将class加载进来了,就能启动一个Activity吗?很显然没有那么简单,Activity有自己的生命周期,她不是一个简简单单的Class类。
接下来就要实现这样一件事,让插件的Activity,躲过系统的PMS检查,让PMS误认为插件的Activity就是宿主的Activity!
未完待续。。。
网友评论