diff --git a/blogs/Android/口水话/插件化、热修复相关口水话.md b/blogs/Android/口水话/插件化、热修复相关口水话.md index 3ce4c5a..544d7fe 100644 --- a/blogs/Android/口水话/插件化、热修复相关口水话.md +++ b/blogs/Android/口水话/插件化、热修复相关口水话.md @@ -13,4 +13,6 @@ 插件化有两种实现方式,静态代理式和 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。 \ No newline at end of file +静态代理式的实现方式很简单,不需要熟悉 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。 \ No newline at end of file