插件化方案对比
0x00 前言
市面上的插件化,已经百花齐放,趋于完善。
主流的开源框架主要的代表有 Alibaba 的 Atlas , 360 的 Replugin 和 DIDI 的 VirtualAPK 。
实现插件化的技术难点
- 代码的加载:
- 资源的加载
- 四大组件动态加载
- 配套的打包插件
0x01 实现原理
1. 代码加载
主要分为两种:第一种是将所有的代码加载在同一个ClassLoader,第二种是插件使用自定义的 ClassLoder 分别加载插件代码。 Atlas 和 Replugin,VirtualAPK 使用的都是后者,使用后者的优势就是控制的粒度更细。
2. 资源的加载
资源的加载要先解决一个问题,就是资源ID 的重复。方案有两种:第一种是使用不同的 Resource 加载插件资源来避免资源ID重复,第二种方式是共用一个 Resource ,通过修改插件的 Resource 的 PP 段来避免资源ID重复。第一种方式编译就不需要过多的参与,修改 PP 段的方式有两种,一种是修改 aapt 的 C 代码 , 生成自定义的aapt。还有一种是直接的对 arsc 进行解析,修改pp段,回写成新的arsc 。看起来更极客,但是难度也更大,需要对arsc 有很深的理解,以及在不同版本下的异同。在这几个框架中,Replugin 使用的是多个 Resource 的方案。 Atlas 使用的是自定义的 aapt ,。VirtualAPK 使用的 Small 方式操作arsc。Replugin对比其他两个方案有个缺点, 他没有公共资源的概念,比如你3个插件都使用了support 包,那它需要把 support 包的资源全部打进 3个插件中。
3. 四大组件:
3.1 VirtualAPK
VirtualAPK 使用的是是预埋的方式。
Activity 当启动 插件 Activity 的会在启动的时候被替换成占坑的 Activity,然后Hook ActivityThread 的 H 和 Instrumentation 类,在对应的回调中替换回插件的 Activity .。达到欺骗系统的效果。
BroadcastReceiver 是将所有的静态注册转成动态注册。
Service 是将所有的插件 service 转成代理的Service ,在Service 的onStartCommand 方法中,重新模拟了插件 service 的声明周期。
ContentProvider 是将所有的插件 ContentProvider 调用转成调用代理的ContentProvider , 然后将真实的 ContentProvider 信息放在Uri 中, 通过解析Uri 获取插件的真实的 ContentProvider 信息。
3.2 Atlas
Atlas 插件的四大组件是直接埋在宿主的 App 的 Manifest 中,通过监听 ClassLoader 的 findClass方法,当是插件的相关 Activity 或者 ContentProvider ,初始化好对应的插件,然后使用对应的插件 ClassLoader 加载,同时 Atlas 也支持动态添加的四大组件,该方式跟VirtualAPK 实现原理一致。
3.2 RePlugin
RePlugin 使用的方式比较新颖,在启动的 Activity 时候替换成了合适的占坑的Activity ,同时记录占坑 Activity 和 真实 Activity 的映射关系,然后ClassLoader的 loadClass方法中,load 占坑 Activity 类的的时候,根据占坑组件跟真实组件的映射关系, 加载真实的组件. 这样的好处是,避免了过多的 Hook 系统组件。
BroadcastReceiver:同VirtualAPK
ContentProvider:同VirtualAPK
Service:是直接在UI线程调用了service 的相关生命周期的方法,同时启动一个Service 来提高service所在进程优先级。
4. 配套的打包插件
待续
0x03 框架对比
1. VirtualAPK:
优点
- 整体代码偏少, 可以符合官方的 "轻量".
- 扩增了android.jar 的api, 使 反射改为直接调用hide api
缺陷
- ContentProvider 支持有问题, 不支持在主进程的ContentProvider
- Activity 支持多进程, 只支持Launch Model
- Service 的多进程只支持两个进程
- BroadcastReceiver 不支持多进程
- 开发有感知, 使用CP 没有在代码实现的时候修改
- 代码实现不优雅, 开发感知明显
- 插件管理逻辑丢失, 需要手动管理. 每次使用插件都要自行加载
- Hook 太多系统的API
- 不支持内嵌插件
- 不支持插件依赖
2. Atlas
优点
- 插件的四大组件是直接声明在宿主中. 所以四大组件多进程支持都是完整的.
- 代码实现比较优雅.,
- 完整的插件管理逻辑
- 支持插件依赖
缺陷
- 宿主打包插件实现比较复杂,hook 了大量 Android Gradle Plugin 2.x 的代码。
- Hook 太多系统的API
3. RePlugin
优点
- 开发的感知少, 得益于编译时期的字节码修改
- 多进程支持完美, 和Activity 的 Launch Model
- Hook 系统api 较少
缺陷
- 资源没法复用, 插件包过大
- 不支持插件依赖
- 需要一个 GuardService 进程,用于管理插件信息。