唯一插件化Replugin源码及原理深度剖析--插件的安装、加载原理

本文深入剖析Replugin的插件安装和加载原理,通过对比系统安装应用的过程,揭示Replugin安装插件时仅移动APK到指定位置并保存插件信息。在加载插件时,涉及dex、so库、资源文件的初始化,以及对插件的Application进行创建和运行。整个流程与系统应用启动相似,展示了Replugin如何实现插件化。
摘要由CSDN通过智能技术生成

上一篇 唯一插件化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

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值