------本文转载自 Android插件化原理解析——插件加载机制
这一系列的文章实在是写的好!
5 Hook ClassLoader
从上述分析中我们得知,在获取LoadedApk的过程中使用了一份缓存数据;
这个缓存数据是一个Map,从包名到LoadedApk的一个映射。正常情况下,我们的插件肯定不会存在于这个对象里面;
但是如果我们手动把我们插件的信息添加到里面呢?系统在查找缓存的过程中,会直接命中缓存!
进而使用我们添加进去的LoadedApk的ClassLoader来加载这个特定的Activity类!这样我们就能接管我们自己插件类的加载过程了!
这个缓存对象mPackages存在于ActivityThread类中;老方法,我们首先获取这个对象:
// 先获取到当前的ActivityThread对象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
// 获取到 mPackages 这个静态成员变量, 这里缓存了dex包的信息
Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
mPackagesField.setAccessible(true);
Map mPackages = (Map) mPackagesField.get(currentActivityThread);
拿到这个Map之后接下来怎么办呢?我们需要填充这个map,把插件的信息塞进这个map里面,
以便系统在查找的时候能命中缓存。但是这个填充这个Map我们出了需要包名之外,
还需要一个LoadedApk对象;如何创建一个LoadedApk对象呢?
我们当然可以直接反射调用它的构造函数直接创建出需要的对象,但是万一哪里有疏漏,构造参数填错了怎么办?
又或者Android的不同版本使用了不同的参数,导致我们创建出来的对象与系统创建出的对象不一致,无法work怎么办?
因此我们需要使用与系统完全相同的方式创建LoadedApk对象;从上文分析得知,
系统创建LoadedApk对象是通过getPackageInfo来完成的,因此我们可以调用这个函数来创建LoadedApk对象;
但是这个函数是private的,我们无法使用。
有的童鞋可能会有疑问了,private不是也能反射到吗?我们确实能够调用这个函数,
但是private表明这个函数是内部实现,或许那一天Google高兴,把这个函数改个名字我们就直接GG了;
但是public函数不同,public被导出的函数你无法保证是否有别人调用它,因此大部分情况下不会修改;
我们最好调用public函数来保证尽可能少的遇到兼容性问题。
(当然,如果实在木有路可以考虑调用私有方法,自己处理兼容性问题,这个我们以后也会遇到)
间接调用getPackageInfo这个私有函数的public函数有同名的getPackageInfo系列和getPackageInfoNoCheck;
简单查看源代码发现,getPackageInfo除了获取包的信息,还检查了包的一些组件;
为了绕过这些验证,我们选择使用getPackageInfoNoCheck获取LoadedApk信息。
5.1 构建插件LoadedApk对象
我们这一步的目的很明确,通过getPackageInfoNoCheck函数创建出我们需要的LoadedApk对象,以供接下来使用。
这个函数的签名如下:
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
因此,为了调用这个函数,我们需要构造两个参数。其一是ApplicationInfo,其二是CompatibilityInfo;
第二个参数顾名思义,代表这个App的兼容性信息,比如targetSDK版本等等,这里我们只需要提取出app的信息,
因此直接使用默认的兼容性即可;在CompatibilityInfo类里面有一个公有字段
DEFAULT_COMPATIBILITY_INFO代表默认兼容性信息;因此,我们的首要目标是获取这个ApplicationInfo信息。
5.2 构建插件ApplicationInfo信息
我们首先看看ApplicationInfo代表什么,这个类的文档说的很清楚:
Information you can retrieve about aparticular application. This corresponds to information collected from theAndroidManifest.xml’s <application> tag.
也就是说,这个类就是AndroidManifest.xml里面的这个标签下面的信息;
这个AndroidManifest.xml无疑是一个标准的xml文件,因此我们完全可以自己使用parse来解析这个信息。
那么,系统