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 进程,用于管理插件信息。