上一篇 唯一插件化Replugin源码及原理深度剖析–唯一Hook点原理
在Replugin的初始化过程中,我将他们分成了比较重要3个模块,整体框架的初始化、hook系统ClassLoader、插件的加载,3个模块已经说了两个,在第一篇的最后是插件的加载,当时没有说,一个是因为篇幅的原因,另一个原因是想从插件的安装、加载、插件apk初始化、整体的流程梳理下来,这里虽然没有分析插件的卸载,但是当看完安装和加载的过程,就自然能明白卸载了。
提示:请不要忽略代码注释,由于通畅上下逻辑思维,不太重要的部分跳转代码不会全部进去一行行的看,但是会将注释写出来,所以请务必不要忽略注释,而且最好是跟着文章一起看源码。
概要:
一、插件的安装,在分析前会先简单介绍系统安装的过程
二、插件的加载及插件apk的初始化,在分析前会简单介绍系统启动应用的过程
等分析完后你会觉得Replugin的安装加载和系统的安装启动原理是那么的类似
一、插件的安装:
Replugin插件的安装并不会真正的处理插件apk中的dex、so库、资源等,只是将插件移动到需要的位置,这个位置默认是宿主的context.getFilesDir(),然后将插件信息包装成Plugin对象并绑定宿主Context、宿主ClassLoader、负责和宿主通信的PluginCommImpl类,最后将Plugin对象存入插件管理进程统一管理。如果你看过PMS的安装过程源码会发现两者及其的类似。
在分析Replugin的安装源码前,先简单描述一下系统PackManagerService安装apk的过程,完了我们在看的时候,可以对比两者是否真的很相似。
apk安装过程描述(5.1源码):
apk的安装可以分为系统自动扫描安装和主动触发PackManagerService的接口安装,但是无论是哪种安装方式,原理都是一样的。
系统扫描安装:
在PMS被创建的时候,在构造方法中会调用scanDirLI方法扫描固定几个文件夹,例如: /system/framework、/system/app、/data/app等目录,在scanDirLI方法中遍历文件夹判断是否是apk文件,如果是会执行scanPackageLI方法,在scanPackageLI方法中为apk文件创建一个PackageParser对象并执行该对象的parsePackage,在PackageParser的parsePackage方法中去解析这个apk文件,主要是解析AndroidManifest.xml,并封装PackageParser.Package对象返回,接着会回到scanPackageLI中最后又调用了一个scanPackageLI的重载方法,这个方法释放apk文件的lib库,优化dex文件,最后将解析得到的数据缓存起来,例如四大组件,Rermission等,系统扫描安装的大概步骤就是这样。
调用PMS接口安装:
入口是PMS的installPackage,这个方法中直接跳到了installPackageAsUser方法中,这个方法中创建了InstallParams对象,这个类继承自HandlerParams,他们都是PMS的内部类,然后发送一条消息到PMS的内部类PackageHandler中调用params.startCopy(),这个方法执行的是HandlerParams中的方法,在这个方法中又执行了InstallParams的handleStartCopy方法,startCopy方法在执行handleStartCopy方法时有失败重试的机制,最多4次,handleStartCopy中会创建InstallArgs对象并执行它的copyApk方法将apk复制到data/app目录下,这一步完成后会再回到startCopy()的方法继续执行handleReturnCode方法,方法中判断如果复制成功后会调用processPendingInstall方法,在这个方法中也会调用解析apk的方法scanPackageLI方法来解析apk,最后会发送一个广播通知安装完成。
Replugin中的插件分为内置插件和外置插件:
内置的插件的安装时在初始化的时候就自动安装和加载了,当时在第一篇文章分析插件框架中的Server端要做的事情时,就有扫描内置插件的过程,在插件管理进程初始化的时候会扫描assest目录下的一个叫plugins-builtin.json的文件,并将插件信息封装成Plugin对象存入PmBase中
的一个叫mPlugins的ConcurrentHashMap中统一管理外置插件的安装需要调用Replugin.install()方法来安装插件,这个过程和内置插件类似,区别就是内置插件是通过assest目录下的json文件来生成插件对象,外置插件则是通过获取插件apk的PackageInfo来生成插件对象,但是并不会处理apk中的dex、so库、资源等,只有当真正使用这个插件中的类时才会去真正的解析加载这个插件。
本文就只说外置插件的安装了,而且在安装方法的注释中说了p-n类型的插件即将废弃,所有这里只说纯apk插件,如果想知道内置插件的安装过程请看第一篇文章
1.插件的安装需要调用Replugin.install()来完成,从这个方法开始
源码路径:com.qihoo360.replugin.RePlugin
public static PluginInfo install(String path) {
if (TextUtils.isEmpty(path)) {
throw new IllegalArgumentException();
}
// 判断文件合法性
File file = new File(path);
if (!file.exists()) {
return null;
} else if (!file.isFile()) {
return null;
}
//省略p-n判断
。。。
//安装插件
return MP.pluginDownloaded(path);
}
2.首先判断了一下需要安装的插件这个文件是否合法,最后直接返回了MP.pluginDownloaded(path)。
源码路径:com.qihoo360.loader2.MP
public static final PluginInfo pluginDownloaded(String path) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "MP.pluginDownloaded ... path=" + path);
}
//针对p-n类型的进程锁,忽略
ProcessLocker lock = null;
try {
//省略p-n类型判断和创建进程锁
。。。
//获取IPluginHost类型对象并调用它的pluginDownloaded方法
PluginInfo info = PluginProcessMain.getPluginHost().pluginDownloaded(path);
//回调
if (info != null) {
RePlugin.getConfig().getEventCallbacks().onInstallPluginSucceed(info);
}
return info;
} catch (Throwable e) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "mp.pded: " + e.getMessage(), e);
}
} finally {
// 去锁
if (lock != null) {
lock.unlock();
}
}
return null;
}
3.上面其实主要的就一句代码,如果看过第一篇的朋友看到这个PluginProcessMain.getPluginHost()方法应该能联想到返回的是谁,我们还是先进去看看
源码路径:com.qihoo360.loader2.PluginProcessMain
public static final IPluginHost getPluginHost() {
if (sPluginHostLocal != null) {
return sPluginHostLocal;
}
// 可能是第一次,或者常驻进程退出了
if (sPluginHostRemote == null) {
// 再次唤起常驻进程
connectToHostSvc();
}
return sPluginHostRemote;
}
4.上面不管是返回sPluginHostLocal还是sPluginHostRemote,其实逻辑不会变,只不过一个是插件管理进程,另一个是其他进程,但是最终获得的都是IPluginHost,如果看过第一篇分析的朋友应该能知道,他是一个Binder对象,它的具体实现类是PmHostSvc,我记得还说这个这个类有点像AMS和ServerManager,如果不太了解的朋友建议先看第一篇框架初始化,那么我们下面就去看PmHostSvc中的pluginDownloaded方法
源码路径:com.qihoo360.loader2.PmHostSvc
public PluginInfo pluginDownloaded(String path) throws RemoteException {
// 通过路径来判断是采用新方案,还是旧的P-N(即将废弃,有多种)方案
PluginInfo pi;
String fn = new File(path).getName();
if (fn.startsWith("p-n-") || fn.startsWith("v-plugin-") || fn.startsWith("plugin-s-") || fn.startsWith("p-m-")) {
//忽略p-n类型
pi = pluginDownloadedForPn(path);
} else {
//执行插件安装
pi = mManager.getService().install(path);
}
//后面会再回来分析这里
if (pi != null) {
// 通常到这里,表示“安装已成功”,这时不管处于什么状态,都应该通知外界更新插件内存表
syncPluginInfo2All(pi);
}
return pi;
}
5.执行安装的是mManager.getService().install(path),这个mManager是PluginManagerServer类型,我们在第一篇的框架初始化中也分析了,它是在PmHostSvc的构造方法中被创建,是用来管理插件的安装、卸载、更新、获取等功能的,我们先看一下mManager.getService()
系统源码:com.qihoo360.replugin.packages.PluginManagerServer
public IPluginManagerServer getService() {
//这个mStub是IPluginManagerServer类型
return mStub;
}
6.返回的是mStub类型,这个mStub是IPluginManagerServer类型,是一个Binder对象,在第一篇框架初始化中也介绍了,它是在PluginManagerServer的构造中被创建的,而且这个Stub是PluginManagerServer的内部类,这个类中的install方法就一句代码,直接调用了外部类PluginManagerServer中的installLocked方法,我们就直接看installLocked方法了
源码路径:com.qihoo360.replugin.packages.PluginManagerS