转载请注明链接:https://blog.csdn.net/feather_wch/article/details/88544574
Replugin源码详解
版本号:2019-03-13(23:59)
文章目录
Replugin的职责划分
1、Replugin分为四个部分的源码
Replugin官方库工程 | 职责 |
---|---|
replugin-host-gradle | 【宿主的gradle插件】 1.动态修改AndroidManifest,生成占坑的四大组件 2.动态生成宿主的配置项HostBuildConfig.java 3.解析内置插件 |
replugin-host-library | 【宿主依赖的核心库】1.初始化整个框架 2.hook ClassLoader 3.加载/启动/管理插件 |
replugin-plugin-gradle | 【插件的gradle插件】1.利用Transfrom API 和Javassit 动态修改字节码,让插件工程的Activity全部继承自Replugin库的xxxActivity 等等 |
replugin-plugin-library | 【插件依赖的库】1. 利用反射来使用宿主的接口和功能 |
2、replugin-host-gradle的职责
1.编译过程中,动态修改AndroidManifest,生成占坑的四大组件。
- 位于
xxx\app\build\intermediates\manifests\full\debug\AndroidManifest
2.动态生成宿主的配置项
HostBuildConfig.java
- 位于
xxx\app\build\generated\source\buildConfig\debug\com\qihoo360\replugin\gen
// RePluginHostConfig.java
public class RePluginHostConfig {
// 常驻进程名字
public static String PERSISTENT_NAME = ":GuardService";
// 是否使用“常驻进程”(见PERSISTENT_NAME)作为插件的管理进程。若为False,则会使用默认进程
public static boolean PERSISTENT_ENABLE = true;
// 背景透明的坑的数量(每种 launchMode 不同)
public static int ACTIVITY_PIT_COUNT_TS_STANDARD = 2;
public static int ACTIVITY_PIT_COUNT_TS_SINGLE_TOP = 2;
public static int ACTIVITY_PIT_COUNT_TS_SINGLE_TASK = 2;
public static int ACTIVITY_PIT_COUNT_TS_SINGLE_INSTANCE = 3;
// 背景不透明的坑的数量(每种 launchMode 不同)
public static int ACTIVITY_PIT_COUNT_NTS_STANDARD = 6;
public static int ACTIVITY_PIT_COUNT_NTS_SINGLE_TOP = 2;
public static int ACTIVITY_PIT_COUNT_NTS_SINGLE_TASK = 3;
public static int ACTIVITY_PIT_COUNT_NTS_SINGLE_INSTANCE = 2;
// TaskAffinity 组数
public static int ACTIVITY_PIT_COUNT_TASK = 2;
// 是否使用 AppCompat 库
public static boolean ACTIVITY_PIT_USE_APPCOMPAT = true;
//------------------------------------------------------------
// 主程序支持的插件版本范围
//------------------------------------------------------------
// HOST 向下兼容的插件版本
public static int ADAPTER_COMPATIBLE_VERSION = 10;
// HOST 插件版本
public static int ADAPTER_CURRENT_VERSION = 12;
}
3.解析内置插件,扫描
assets/plugins
,解析包含文件名、包名、版本、路径的plugins-builtin.json
- 位于
xxx\app\build\intermediates\assets\debug
// plugins-builtin.json
[
{
"high":null,
"frm":null,
"ver":1,
"low":null,
"pkg":"com.hao.repluginlogin",
"path":"plugins/login.jar",
"name":"login"
}
]
3、replugin-host-library的职责
- 初始化整个框架
- hook ClassLoader
- 加载/启动/管理插件
4、replugin-plugin-gradle的职责
- 利用
Transfrom API
和Javassit
动态修改字节码,让插件工程的Activity全部继承自Replugin库的xxxActivity- 动态将插件Apk中调用
LocalBroadcastManager
的地方替换为Replugin库中的PluginLocalBroadcastManager
- 动态将
ContentReolver、ContentProviderClient
的调用替换成Replugin库中对应的调用- 动态修改插件工程中所有调用
Resource.getIdentifier()方法
的地方,将第三个参数修改为插件工程的包名
5、replugin-plugin-library的职责
- 利用反射来使用宿主的接口和功能
初始化
核心原理
1、Replugin的核心底层原理?如何做到宿主和插件之间的通信和数据共享?
- 使用Binder机制
- 类似于
ServiceManager和AMS
- 将一个常驻进程或者宿主的主进程作为
Server端
(根据配置选择)- 其他的
插件进程
和宿主进程
全部属于Client端
Server端
:创建了一个Provider,通过Provider的query
方法返回Binder对象
来实现多进程之间、宿主和插件之间的通信和数据共享
- Server端作为插件管理进程,任务包括:插件的安装、卸载、更新、状态判断
2、Server端做了哪些事情?
- 创建了一个Provider,通过Provider的query()方法返回
Binder对象
来实现多进程之间、宿主和插件之间的通信和数据共享
- 处理: 插件的安装、卸载、更新、状态判断、提取优化dex文件
- 分配坑位
- 启动坑位
3、Replugin通过Hook ClassLoader如何处理Activity的启动?
- 能够对系统的类加载过程进行拦截
- 将之前分配的坑位信息替换为
真正要启动的组件信息
- 并且使用该组件对应的ClassLoader进行类的加载
4、Replugin 启动四大组件的大致流程
- Client端,通知Server端,进行:
- 检查插件是否安装
- 安装插件
- 提取并优化dex文件
- 分配坑位
- 启动坑位(将真实组件的信息替换为坑位的信息)
- 利用坑位欺骗系统的检查
- 通过检查后Client开始真正加载目标组件
- 用Hook的ClassLoader对系统的类加载过程进行拦截
- 将坑位信息替换为真实组件的信息,并且用与其对应的ClassLoader进行类的加载
5、为什么要用与真实组件对应的ClassLoader去加载该组件?
关键字
Binder
ServiceManager和AMS
Server/Client
ContentProvider
ClassLoader
RePluginApplication
1、App在应用端所有的回调方法中,最早的一个回调方法是什么?
- Application的
attachBaseContext()
中- 该方法时
ContextWrapper
中的方法,在Application创建后,调用attach()
时回调该方法- 因此插件化框架中对系统ClassLoader等一切系统组件的Hook,都必须该方法中处理。避免不必要的故障。
2、RePluginApplication的源码分析
- 提供创建配置对象的方法
- 提供可创建RepluginCallbacks回调的空方法
- attachBaseContext()
- onCreate()
public class RePluginApplication extends Application {
// 1、创建配置对象,提供可选的配置。
protected RePluginConfig createConfig() {
return new RePluginConfig();
}
// 2、创建RePluginCallbacks,能用于处理,例如插件不存在,启动下载流程,下载插件Apk
protected RePluginCallbacks createCallbacks() {
return null;
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
RePluginConfig c = createConfig();
if (c == null) {