|
|
|
@ -9,10 +9,16 @@ |
|
|
|
|
|
|
|
|
|
#### 热修复 |
|
|
|
|
|
|
|
|
|
热修复,我所了解的一种实现方式就是类加载方案,即 dex 插桩,这种思路在插件化中也会用到。除此之外,还有底层替换方案,即修改 替换 ArtMethod。采用类加载方案的主要是以腾讯系为主,包括微信的 Tinker、饿了么的 Amigo;采用底层替换方案主要是阿里系的 AndFix 等。 |
|
|
|
|
|
|
|
|
|
#### 插件化 |
|
|
|
|
|
|
|
|
|
插件化有两种实现方式,静态代理式和 Hook 式。 |
|
|
|
|
插件化无非就是解决类加载和资源加载的问题,资源加载一般都是通过反射 AssetManager。所以按照类加载的划分来说,插件化有两种实现方式,静态代理式和 Hook 式。 |
|
|
|
|
|
|
|
|
|
静态代理式的实现方式很简单,不需要熟悉 Activity 的启动流程啥的,直接采用面向接口的编程方式即可。首先需要宿主 App 加载 plugin.apk 构造 DexClassLoader 和 Resource 对象,有了 DexClassLoader 就可以加载插件里面的类了,Resource 是通过反射 AssetManager 的 addAssetPath 创建一个 AssetManager,再构造 Resource 对象,有了 Resource 对象就可以访问到资源文件了。但是这时插件是没有 Context 的环境的,这个上下文需要宿主提供给它。具体做法是通过 PackageManager 获取插件的入口的 Activity 进行注入宿主的 Context,这就完成了宿主 App 跳插件 App 的步骤。但是插件 App 是没有上下文环境的,所以在插件 App 里面 Activity 直接的跳转是不能直接 startActivity 的,需要拿宿主的 Context 执行 startActivity,同理,其他一些需要获取 Context 执行的操作都要通过宿主的 Context 去执行,比如弹 Toast、解析 layout 文件等等。任玉刚的 dynamic-load-apk 就是采用这种方式,也被称为 that 框架,这个 that 就指向宿主的 Context。 |
|
|
|
|
|
|
|
|
|
静态代理式实现简单,也无需考虑版本兼容性,但是每次编写插件类时都要小心要使用宿主的 Context,所以很不方便。Hook 式是把插件里面的 dex 文件直接复制到宿主的 dexElements 里面,这样就有了宿主的上下文环境了。但是现在问题就转变成了如何启动未在 Manifest 中注册的 Activity,所以就需要 Hook AMS 来绕过检查。但是需要注意的是版本兼容性,不同的 Android 版本 Hook 点不同,需要做兼容。在 Android 8 以下版本就是 Hook ActivityManagerNative 拿到 IActivityManager 接口,然后做动态代理,其他版本也是类似思想。在 Android 8-10,是 Hook ActivityManager,Android 10 以上就是 Hook ActivityTaskManager。 |
|
|
|
|
静态代理式实现简单,也无需考虑版本兼容性,但是每次编写插件类时都要小心要使用宿主的 Context,所以很不方便。Hook 式是把插件里面的 dex 文件直接复制到宿主的 dexElements 里面,这样就有了宿主的上下文环境了。但是现在问题就转变成了如何启动未在 Manifest 中注册的 Activity,所以就需要一个占位的 Activity 并且 Hook AMS 来绕过检查。但是需要注意的是版本兼容性,不同的 Android 版本 Hook 点不同,需要做兼容。在 Android 8 以下版本就是 Hook ActivityManagerNative 拿到 IActivityManager 接口,然后做动态代理,其他版本也是类似思想。在 Android 8-10,是 Hook ActivityManager,Android 10 以上就是 Hook ActivityTaskManager。 |
|
|
|
|
|
|
|
|
|
在简单的说这两种方式,静态代理式是自己构造一个 DexClassLoader,所以没有上下文环境,上下文环境需要宿主提供给它。一个 DexClassLoader 就包含一个插件。而 Hook 式是把插件的里面的 dex 文件合并到宿主的 DexClassLoader 里面,但是得绕过 AMS 的未在 Manifest 中注册的 Activity 会抛出的 ClassNotFoundException,所以就需要 Hook startActivity 和 handleLauncherActivity。前者实现简单,兼容性好,而且每个插件是分离,后者兼容性差但是开发方便,但是如果多个插件里面有相同的类,就会 GG 了。 |
|
|
|
|
|
|
|
|
|
显然,每个插件独立的 DexClassLoader 具有先天优势,这其实就引入了另一个 Hook 式的实现方式,即构建 LoadedApk 式。但是这种实现方式困难,而且兼容性巨差,基本上每新出个版本就得兼容 PackageParse 类,这个类其实我们在做静态代理式也用到了,解析静态广播转变为动态广播。 |