在上一章节中我们讲到了Replugin初始化的时候创建了插件管理进程作为服务端。其他工作进程作为客户端,然后分别在各自的进程中初始化自己要做的事情。创建了多个Binder对象用来完成客户端和服务端的信息交互。在完成PmBase 的初始化后。在PMF.init()中还有剩下的部分代码
com.qihoo360.loader2.PMF
public static final void init(Application application) {
setApplicationContext(application);
PluginManager.init(application);
sPluginMgr = new PmBase(application);
sPluginMgr.init();
Factory.sPluginManager = PMF.getLocal();//1
Factory2.sPLProxy = PMF.getInternal();//2
PatchClassLoaderUtils.patch(application);//3
}
"1"中PMF.getLocal()就是在PmBase 构造函数中 创建的PluginCommImpl。这个类负责宿主与插件、插件间的互通,因此这个类中的方法使用的频率会比较高。而Factory类是一个包装类,缓存了PluginCommImpl对象,方便以后的调用。同样的"2"中的PMF.getInternal() 就是在PmBase 中创建的PluginLibraryInternalProxy。同样的这个类中的方法也会经常使用到,比如说startActivity这个方法。
"3"PatchClassLoaderUtils.patch(application)这里的逻辑就涉及到这个框架的核心唯一hook 的点,hook系统的ClassLoader
Replugin唯一hook点 hook系统ClassLoader
Replugin是如何hook 系统的ClassLoader。
//com.qihoo360.loader.utils.PatchClassLoaderUtils
public class PatchClassLoaderUtils {
public static boolean patch(Application application) {
try {
// 获取Application的BaseContext (来自ContextWrapper)
Context oBase = application.getBaseContext();
if (oBase == null) {
return false;
}
// 获取mBase.mPackageInfo
// 1. ApplicationContext - Android 2.1
// 2. ContextImpl - Android 2.2 and higher
// 3. AppContextImpl - Android 2.2 and higher
Object oPackageInfo = ReflectUtils.readField(oBase, "mPackageInfo");
if (oPackageInfo == null) {
return false;
}
// mPackageInfo的类型主要有两种:
// 1. android.app.ActivityThread$PackageInfo - Android 2.1 - 2.3
// 2. android.app.LoadedApk - Android 2.3.3 and higher
// 获取mPackageInfo.mClassLoader
ClassLoader oClassLoader = (ClassLoader) ReflectUtils.readField(oPackageInfo, "mClassLoader");
if (oClassLoader == null) {
return false;
}
// 外界可自定义ClassLoader的实现,但一定要基于RePluginClassLoader类
ClassLoader cl = RePlugin.getConfig().getCallbacks().createClassLoader(oClassLoader.getParent(), oClassLoader);
// 将新的ClassLoader写入mPackageInfo.mClassLoader
ReflectUtils.writeField(oPackageInfo, "mClassLoader", cl);
// 设置线程上下文中的ClassLoader为RePluginClassLoader
// 防止在个别Java库用到了Thread.currentThread().getContextClassLoader()时,“用了原来的PathClassLoader”,或为空指针
Thread.currentThread().setContextClassLoader(cl);
} catch (Throwable e) {
e.printStackTrace();
return false;
}
return true;
}
}
这里利用了反射工具,是代码看上去非常简洁。主要做了这么几件事情
1. application.getBaseContext() 获取APP的BaseContext
2.利用反射获取BaseContext 中的mPackageInfo字段,他的类型是LoadedApk类型
3.利用反射获取mPackageInfo 中的mClassLoader字段。
4.通过宿主的父ClassLoader和宿主ClassLoader利用RePluginCallbacks的createClassLoader方法生成RePluginClassLoader
5.替换mPackageInf