插件化

Android 插件化技术,可以实现功能模块的按需加载和动态更新,其本质是动态加载未安装的 apk 。
Android 程序每次更新都要下载一个完整的 apk,而很多时候软件只是更新了一 个小功能而已,这样的话,就显得很麻烦。如果把 android 程序做成主程序+插 件化的形式呢,这样才利于小功能的扩展(比如一般 App 的皮肤样式就可以看 成一个插件)。

主要原理

插件化要解决的三个核心问题:类加载、资源加载、组件生命周期管理

类加载

主要利用 Java ClassLoader 的原理,如 Android 的 DexClassLoader,可动态加载的内容包括 apk、dex、jar 等。在Android中,常用的两种类加载器:PathClassLoader 和 DexClassLoader,它们都继承于 BaseDexClassLoader 。

1
2
3
4
5
6
7
8
9
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
public PathClassLoader(String dexPath, String librarySearchPath,
ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}
1
2
3
4
5
6
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String
librarySearchPath, ClassLoader parent) {
super(dexPath, null, librarySearchPath, parent);
}
}

说明:

  • DexClassLoader 的 构 造 函 数 比 PathClassLoader 多 了 一 个 , optimizedDirectory 参数,这个是用来指定 dex 的优化产物 odex 的路径, 在源码注释中,指出这个参数从 API 26 后就弃用了。
  • PathClassLoader 主要用来加载系统类和应用程序的类,在 ART 虚拟机上 可以加载未安装的 apk 的 dex,在 Dalvik 则不行。
  • DexClassLoader 用来加载未安装 apk 的 dex。

资源加载

Android 系统通过 Resource 对象加载资源,因此只需要添加资源 (即 apk 文件)所在路径到 AssetManager 中,即可实现对插件资源 的访问。

1
2
3
4
5
6
7
8
9
// 创建 AssetManager 对象
AssetManager assetManager = new AssetManager();
// 将 apk 路径添加到 AssetManager 中
if (assetManager.addAssetPath(apkPath) == 0) {
return null;
}
// 创建插件 Resource 对象
Resources pluginResources = new Resources(assetManager, metrics,
getConfiguration());

说明:由于 AssetManager 的构造方法时 hide 的,需要通过反射区创建 。

组件生命周期管理

对于 Android 来说,并不是说类加载进来就可以使用了,很多组件 都是有“生命”的;因此对于这些有血有肉的类,必须给他们注入活力,也就是所谓的组件生命周期管理。 在解决插件中组件的生命周期,通常的做法是通过 Hook 相应的系统对象,实现欺上瞒下,后面将通过 Activity 的插件化来进行讲解。

Activity插件化

四大组件的插件化是插件化技术的核心知识点,而 Activity 插件化 更是重中之中,Activity 插件化的主流实现方式是通过 Hook 技术实现。
具体步骤如下:

  1. Activity1 调 用 startActivity , 实 际 会 调 用 Instrumentation 类 的 execStartActivity 方法,Instrumentation 是系统用来监控 Activity 运行的 一个类,Activity 的整个生命周期都有它的影子。
  2. 通过跨进程的 binder 调用,进入到 ActivityManagerService(AMS)中, 其内部会处理 Activity 栈。之后又通过跨进程调用进入到 Activity2 所在的进程中。
  3. ApplicationThread 是一个 binder 对象,其运行在 binder 线程池中,内部 包含一个 H 类,该类继承于 Handler。ApplicationThread 将启动 Activity2 的信息通过 H 对象发送给主线程。
  4. 主线程拿到 Activity2 的信息后,调用 Instrumentation 类的 newAcitivity 方法,其内部通过 ClassLoader 创建 Activity2

插件化的优势

  • 适应并行开发,解耦各个模块,避免模块之间的交叉依赖,加快编译速度, 从而提高并行开发效率。
  • 满足产品随时上线的需求  修复因为我们对自己要求不严格而写出来的 bug。
  • 插件化的结果:分为稳定的 release 版本和不稳定的 snapshot 版本,每 个模块都高度解耦,没有交叉依赖,不会出现一个模块依赖了另一个模块, 其中一个人改了这个模块的代码,对另一个模块造成影响。

适合于项目超级大 但是功能相对不集中。比如 一个支付宝 App 里面即包 含共享单车 也包含 电影票。这种与本业务完全不同的可以做成插件化。

插件化的弊端

每一个插件都是一个 apk,插件多的时候管理起来也麻烦。

插件化要处理的细节非常多,不仅要适配不同版本的 Android 系统,还要适配国
产的各种 ROM。要深入学习插件化的各种解决方案,可以去探索开源的插件化
框架。
2018 年 Android 9.0 上 Android 开始对私有 API 的使用进行限制,所以后面插件
化可能退出历史主流,但是了解插件化涉及到的知识和技术,可以更好的理解
Android 系统