大家好,我系苍王。
以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。
Replugin,为何我选择要研究这个的插件呢?很大的原因是因为它的介绍中说明,他只会有一个hook点。
一.Hook
hook点是什么?
我们入门Android的时候,一定会看到过这个图,但是你确定深刻了解到这个图的吗?
我们试着换着思维,用组件化的角度去看这张图,你会发现其层级依赖关系,与组件化的工程是非常类似的。(当然你看得角度不同,我只能说是类似)
倘若你用插件的思想来看,我们上层的众多应用(Applications)就是作为framewrok层的插件一样的存在。(Framework作为宿主一样的存在,只是它是对于应用们,他说了算,基于安全管理和效率,他能限制你们获取资源的权限和方法)
然后我们想要在独自的应用中做得像Framework层一样的加载应用,我们本身就享用了Framework层对资源等的加载机制。如果我们想要将某些资源加载,加载验证,权限限制等绕开,运用到手机本身Framework来完成,就会涉及到hook了。
以我理解:hook,当Framework运行时,拦截并替换掉Framework层中某些原生的方法或对象,让其运行到我们想要的效果。
滴滴的VirtualApk 会hook掉AMS(ActivityManagerService)和Instumentation,这两个Framework的文件非常重要,AMS是四大组件的入口,管理生命周期,管理应用通信等,Instumentation管理了Activity的生命周期的调用。有兴趣可以深入去看这两个Framework文件。而其他旧式插件化,还会hook掉一下Service,Broadcast的机制。
而Replugin却走的是完全和传统插件化不同的路,它hook掉的是ClassLoader,而且它只有唯一的hook点。
hook为何会不安全?
就是拦截和替换原生机制,因为Android的机型太多了,而且是开源的,那么各个厂商定制Rom的修改,不同版本的适配也是非常大的阻碍,倘若替换掉某些厂商修改Google原生Android的源码里面的方案,2️⃣刚好hook点没有兼容这方面的源码,就有引起hook失败或者崩溃的可能性。
意味着hook点越少,其可能产生修改的代码会变小,维护的代价会变小,耗损的人力资源就会变少,整个用户体验就会提升。
架构的标准,是要懂得,衡量 时间+空间+效率。唯一hook点,暂时Replugin应该是插件化hook点的极致了。
二.ClassLoader
对于Android 的ClassLoader,请认真仔细看下图,一张图为你解答各种疑问。
此图非常形象的说明Android里面ClassLoader的架构。
这里一般插件化框架,都是使用DexClassLoader动态加载dex文件。
DexClassLoader可以加载apk,dex,jar,还有zip后缀格式的文件,其最终应该是加载dex文件,这也是我QQ群中验证问题的答案。
BaseDexClassLoader,里面有一个DexPathList对象,是用来保存dex的列表的,而查询dex里面的资源、class都是在这个列表中遍历dex对象。
而Replugin是特别的其使用自定义的PathClassLoader来加载apk中的dex,其有别于其他插件框架。
三.唯一hook点
我们看一下Replugin的加载过程。
用的是官网的replugin-host-library为例子
Application中attachBaseContext是最快执行的,其调用了Replugin.App.attachBaseContext方法
他会调用到PMF这个类init方法,PMF是框架和住程序接口
其里面会调用pluginManger的初始化,还有PatchClassLoaderUtils.patch的方法。
PatchClassLoaderUitls这个类是修改宿主和私有属性的位置,其实就是那个唯一的hook点的位置
可以看到在patch中需要获取到整个application的context对象。
然后生成自己的classLoader对象,去hook掉mClassLoader对象,FieldUtils是对Java反射机制的封装,以后使用到发射的时候,可以参考一下这里的封装,感觉是迄今看到众多插件化的封装最完善的。
这里的对参数的说明很清晰,最终会返回RePluginClassLoader对象。
这里调用到RepluginClassLoader里面,copyFromOrginal会将一些需要更改的属性去掉final属性,才能开始修改。initMethods,反射来替换掉ClassLoader的方法
里面替换掉四个方法,findResource和findResources是资源的获取,findLibrary是库获取,getPackage就是获取包信息了。
这里MethodUtils是方法反射的封装,也是封装得非常好。
在Android源码中,是通过DexPathList中读取资源,Resource是以URL格式返回,而lib库是用String返回
包获取是在classLoader中完成的
这里你会发现有点矛盾的地方,因为有可能不是那么容易一下子能看明。
我们前面看到需要反射出一些BaseDexLoader的一些方法
但是你看到其继承调用的时候,却使用了一模一样的方法,这样不就用了一模一样的流程了?这有何意义?
这里其实意义就在于其传入的mOrig参数,这个参数是classLoader的对象,而不同插件间都有classloader,那么其分别调用插件间的classloader资源的时候,就需要在这里拦截调用。
以上就是对Replugin的唯一hook点的分析。
值得借鉴的地方。
(1)唯一hook点,是用hook掉足够大的ClassLoader对象,来让资源加载得到更加便利。
(2)Java反射机制的整合FieldUtils域反射封装和MethodUtils方法反射封装。
这里发一条广告,我创立了一个小密圈。
每天会分享java基础,组件化插件化相关,音视频开源项目分享。
最重要,带有群员们珍贵的分析和点评。
还有最新插件化框架的分析和运用,组件化架构技巧的分享,都尽在这里。
不需要一顿饭的价钱 ,68/一整年的学习资料。
付费是真干货,真正节省时间的通往技术的途径。
来到这里希望影响你的不只是技术提升,而且是人生的架构的导向。
成立两周已经有超过190+的小伙伴加入了我们的行列了,并且持续增加,让我们一起成长,群内还有不定时福利,包括原创书籍的福利哦。
我建立了一个关于Android架构学习的群,里面可以进一步进行组件化学习的交流。
群号是316556016,也可以扫码进群。我在这里期待你们的加入!!!