一、前言
如果使用过一些三方库的都知道,大部分引入库都是在Application中初始化的,所以在阅读Replugin源码前,不用看官方宿主接入wiki基本就能猜到,肯定是在替换自定义Application中对插件框架初始化的,本章就是以此为契入点,走读源码展开整体框架初始化流程分析,由于整个Replugin架构逻辑还是相当复杂的,为了方便走读逻辑,附上的代码几乎都添加了注释说明方便大家理解,大家可不要忽视哦!!!!!
Replugin源码地址: https://github.com/Qihoo360/RePlugin
(由于Replugin官方提供的项目不支持clone直接编译运行,为了方便代码走读和编译,直接使用了官方FAQ中提供的一位大神的构建升级版本,代码可能与Replugin最新版本有轻微出入,但是主体流程变化不大,参考:https://note.youdao.com/share/?id=61c0f27494c5a466a40642829da2938c&type=note#/)
二、框架初始化
第1步:RepluginApplication就是Replugin提供给三方应用来继承的Application类,继承了RepluginApplication后,当应用进程被系统创建时就会自动调用attachBaseApplication方法(至于为什么会自动回调attachBaseApplication,不明白的同学自行百度啦),OK,锁定这里,Replugin的初始化逻辑就是放在这里.
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
RePluginConfig c = createConfig();
if (c == null) {
c = new RePluginConfig();
}
RePluginCallbacks cb = createCallbacks();
if (cb != null) {
c.setCallbacks(cb);
}
//1.框架初始化入口
RePlugin.App.attachBaseContext(this, c);
}
第2步
:RePlugin.App.attachBaseContext是一个内部类方法,会执行所有的初始化的相关逻辑,其中IPC.init方法会去区分插件管理常驻进程和非常驻进程,不同进程在这里的实现逻辑是不一样的,这个下面会详细说明。另外还要注意的一点是利用HostConfigHelper反射获取动态编译自动生成在gen目录下的RePluginHostConfig.java文件,以便初始化的时候根据配置的参数展开。
RePlugin.java
public static void attachBaseContext(Application app, RePluginConfig config) {
...
//1. 初始化应用自己的classloder和ApplicationContext,方便框架内部调用
RePluginInternal.init(app);
sConfig = config;
//2. RepluginConfig在这里初始化其内部RePluginCallbacks,RePluginEventCallbacks这两个对象默认值
//三方应用在继承插件框架的时候也可以外部set
sConfig.initDefaults(app);
//3. 根据当前进程名来判断是UI进程还是专门管理插件的常驻进程,常驻进程默认名称是:GuardService,可以外部set
IPC.init(app);
//4. 这里通过调用HostConfigHelper静态方法来加载初始化自己,会通过反射读取gradle动态编译自动生成的配置类RePluginHostConfig数据
//比如是否使用APPCOMPAT、坑数量等等
HostConfigHelper.init();
// FIXME 此处需要优化掉
AppVar.sAppContext = app;
// Plugin Status Controller
PluginStatusController.setAppContext(app);
//5. 主要初始化核心类PmBase,按照常驻进程和UI进程来初始化不同逻辑
PMF.init(app);
//6.主要执行插件挂载操作
PMF.callAttach();
sAttached = true;
}
第3步:
PMF.init方法中会去初始化PmBase sPluginMgr,这个类很重要,是整个框架初始化流程中个人认为最核心的类,后面很多地方都会看到此类身影,后面会详细说明。同时,Replugin宣传中最大的"卖点"特性:唯一Hook点,就是在这PatchClassLoaderUtils.patch方法中实现的,Hook的流程会在后面单独抽出一篇博文来讲解。
PMF.java
public static final void init(Application application) {
setApplicationContext(application);
//1.主要是初始化sPluginProcessIndex值,这个值主要用来区分UI进程和插件自定义进程
//插件进程名称类似 xxx.xx:p0, xxx.xx:p1, xxx.xx:p2
// 如果是UI进程则返回-1,其他进程则返回-100,-99,-98
PluginManager.init(application);
//2. 初始化框架核心类PmBase,构造函数中会初始化生成Activity Service Provider的相关“坑位”
// 初始化init时会区分插件管理常驻进程和非常驻进程,常驻进程中会去扫描所有内置插件并存储在PmBase内部的一个Map中
sPluginMgr = new PmBase(application);
sPluginMgr.init();
//3. 将PmBase的mLocal和mInternal对象赋值给Factory和Factory2,后面加载插件初始化的时候会用到
Factory.sPluginManager = PMF.getLocal();
Factory2.sPluginManager = PMF.getInternal();
//4. Replugin唯一hook点 hook系统ClassLoader
PatchClassLoaderUtils.patch(application);
}
第4步:
上面提到过,PmBase这个类很核心,重点讲解下,首先先走读下new PmBase()的代码,看看PmBase构造函数中干了哪些事情。
PmBase(Context context) {
//
mContext = context;
// TODO init
//init(context, this);
//1.如果当前进程是UI进程或者plugin进程,初始化Provider和Services的临时“坑位”
if (PluginManager.sPluginProcessIndex == IPluginManager.PROCESS_UI || PluginManager.isPluginProcess()) {
String suffix;
if (PluginManager.sPluginProcessIndex == IPluginManager.PROCESS_UI) {
suffix = "N1";
} else {
suffix = "" + PluginManager.sPluginProcessIndex;
}
//com.XXX.XXX.loader.p.Provider.N1 //com.XXX.XXX.loader.p.Provider.0 或者 com.XXX.XXX.loader.p.Provider.1
//mContainerProviders主要是用来存储宿主Provider坑位的,区分进程
mContainerProviders.add(IPC.getPackageName() + CONTAINER_PROVIDER_PART + suffix);
//com.XXX.XXX.loader.s.Service.N1 //com.XXX.XXX.loader.s.Service.0 或者 com.XXX.XXX.loader.s.Service.1
//mContainerServices要是用来存储宿主Service坑位的&#x