阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
本文通过Activity调用原理来解读Replugin插件化技术
一、开启插件Activity流程
第1步
开启插件Activity的入口在Replugin.StartActivity(Context context, Intent intent),其他intent内部一定要传入想要开启的插件名。
Factory.startActivityWithNoInjectCN内部调用sPluginManager.startActivity方法,那么sPluginManager是哪里传进去的?
PMF.java
看下PMF.init的方法可以看到sPluginManager是PMF内部维护的sPluginMgr.mLocal,也就是Pmbase内部的PmLocalmpl mLocal对象,继续跟踪发现真正实现startActivity逻辑的是PmBase内部的PmInternalmpl.startActivity下面这个方法。
第2步
重点走读下上面startActivity方法,首先会判断要跳转的插件是否已经存在了,如果不存在则回调callback接口去提示用户下载等逻辑操作,如果插件状态不正确,则回调外部callback去提示用户插件不可用或者去升级,如果插件首次加载并且是大插件则异步加载并弹窗显示正在加载。然后在调用context.startActivity方法前会去通过下面的loadPluginActivity方法将目标插件Activity class替换为“坑位”Activity,这样其实传给AMS的还是宿主的坑位Activity。
PmInternalmpl.java
坑位Activity的替换规格,逻辑是在PmLocalmpl.allocActivityContainer方法中执行,这个有兴趣的可以自己去跟踪下,坑位Activity替换规格,这里就不深入去分析了。
第3步
上述几步使用坑位Activity来替换目标Activity之后,AMS内部都是基于这个坑位Activity来实现对其合法性、生命周期进行回调啦,AMS一系列流程走完,然后AMS再通过宿主进程的ActivityThread提供的IApplicationThread Binder代理对象去实现宿主进程中类加载坑位Activity啦,并对其回调相关接口如onCreate。正常startActivity是这样没错,但是我们明明是希望调用的宿主的Activity啊,又不是坑位Activity?是的这里需要对坑位Activity进行恢复真身处理,恢复为目标插件Activity。在哪里恢复呢?没错,就是在我们唯一hook点:RePluginClassLoader中。
我们在《Replugin插件化技术解读之框架初始化、插件安装与加载(二)》一文中已经分析的很明白,从RePluginClassLoader的loadClass方法中会首先去使用PMF.loadClass去加载插件,去生成插件的Loader对象,
初始化好插件的DexClassLoader、Resource、PluginContext等资源,然后用插件的ClassLoader去尝试加载目标Activity。具体执行逻辑在PluginProcessPer.resolveActivityClass方法中。
这样我们插件目标Activity就恢复成功,目标Activity内部逻辑就能正常执行啦。
总结:
开启插件Activity的大致流程为:
插件Activity -> Replugin.startActivity -> 解析出插件Activity对应的pluginName -> 挑选坑位Activity替换 ->调用系统startActivity方法 -> AMS回调执行坑位Activity类加载 ->加载并初始化目标插件资源、上下文、类加载器(首次) ->Hook掉ClassLoader的类加载中恢复为插件Activity -> 插件Activity启动,并拥有完整生命周期。
二、插件内部startActivity流程
插件中startActiviy有两种场景:调用自己插件本身的Activity或者调用宿主的Activity。显然光凭插件自己是无法成功开启的。因为插件的Manifest根本就没有被AMS识别到呀,必须还是要用统一的Replugin.startActivity接口去按照上述分析的流程重走一遍才行的啊。
在《Replugin插件化技术解读之目录结构解读》一文中,我们了解到其实插件在动态编译过程中会在字节码层做适量修改,如插件的所有Activity都会被继承replugin-plugin-lib中的PluginActivity这样一个基类。所以我们插件内部其实Activity内部最后执行startActivity都会先走PluginActivity的此方法;
PluginActivity.java
显然,反射的是Factory2中的startActivity方法,而Factory.startActivity其实就是调用的PmInternalmpl.startActivity方法,显然这个跟上面宿主启动插件Activity对上啦。
那么RepluginInternal.initLocked方法是什么时候走的呢?容易看到是在RepluginFramework.init中调用,而RepluginFramework.init初始化时在Entry的create方法中,这里是不是很熟悉啦。没错,在《Replugin插件化技术解读之框架初始化、插件安装与加载(二)》一文中我们知道在加载插件的时候会通过回调执行插件的Entry的create方法初始化插件,显然这里这条线又连上啦。
这样插件内部启动startActivity说白了就是通过反射调用宿主Factory2中startActivity方法去重走了遍上述分析的标准启动插件Activity流程。注意,这里其实仅仅是通过插件Activity继承PluginActivity重写了Activity内部StartActivity方法,改变了Activity内startActivity方法启动步骤。
我们显然还要修改Context上下文对应的startActivity方法才能保证这里调用的时候也会走到我们设定好的宿主startActivityq启动流程,
那么插件内部其他startActivity的地方都要进行这样的调整才行,这个就大家自己跟下就能明白了,原理差不多。
PluginActivity的attachBaseContext方法中我们会使用RepluginInternal.createActivityContext方法,通过反射,最终吊起来PmInternalImpl.createActivityContext方法。创建对应插件的PluginContext对象,PluginContext前文已经介绍,然后用这个PluginContext替换掉插件内部的上下文BaseContext,其实也就是Activity继承的ContextWrapper类的Context mBase对象。跟踪Android源码很容易发现,ContextWrapper内部封装了startActivity startService等方法,所以这里我们要将这个Context替换掉。那么PluginContext内部又做了什么呢?显然它也重写了startActivity方法。
这里 super.startActivity其实就是对应宿主上下文了,直接走宿主startActivity流程。
其他还有在PluginApplicationClient为插件创建的Application对象,在PluginApplicationClient.callAttachBaseContext方法中使用反射调用了Application.attachf方法,用PluginContext对象替换了原来的Context,这个就各位看官自己去梳理了哈。
原文链接https://blog.csdn.net/hellogmm/article/details/79058135
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680