通过Replugin 记录(二)插件启动过程,我们知道插件中的Activity被gradle修改为继承PluginActivity,其中startActivty被重写,通过Factory2.startActivity显式调用Replugin启动插件activity的方法
public void startActivity(Intent intent) {
//反射调用Factory2.startActivity
if (RePluginInternal.startActivity(this, intent)) {
// 这个地方不需要回调startActivityAfter,因为Factory2最终还是会回调回来,最终还是要走super.startActivity()
return;
}
super.startActivity(intent);
}
除此之外还可以通过RePlugin.startActivity()在宿主启动插件activity。这两个方法最终都会调用Factory类的startActivityWithNoInjectCN。
/**
* 内部接口,仅为Factory2.startActivity(context, intent) 和 RePlugin.startActivity方法而使用
*
* @param context 应用上下文或者Activity上下文
* @param intent Intent对象
* @param plugin 插件名
* @param activity 待启动的activity类名
* @param process 是否在指定进程中启动
* @return 插件机制层,是否成功,例如没有插件存在、没有合适的Activity坑
* Added by Jiongxuan Zhang
*/
public static final boolean startActivityWithNoInjectCN(Context context, Intent intent, String plugin, String activity, int process) {
boolean result = sPluginManager.startActivity(context, intent, plugin, activity, process);
RePlugin.getConfig().getEventCallbacks().onStartActivityCompleted(plugin, activity, result);
return result;
}
PluginCommImpl.startActivity
/**
* 启动一个插件中的activity,如果插件不存在会触发下载界面
* @param context 应用上下文或者Activity上下文
* @param intent
* @param plugin 插件名
* @param activity 待启动的activity类名
* @param process 是否在指定进程中启动
* @return 插件机制层是否成功,例如没有插件存在、没有合适的Activity坑
*/
public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "start activity: intent=" + intent + " plugin=" + plugin + " activity=" + activity + " process=" + process);
}
return mPluginMgr.mInternal.startActivity(context, intent, plugin, activity, process, true);
}
在开始看startActivity之前,我们要理一下一般插件化启动插件Activity做的工作。
1、把要启动的目标Activity替换成注册在mainfest上的占坑Activity(其中涉及进程选择启动,坑位选择分配)
而替换的原因是系统在startActivity的过程中会根据intent去PackageManagerService查询我们要启动的activity是否已经在mainfest中注册了。(PluginLibraryInternalProxy.startActivity)
2、在startActivity流程从ActivityManagerService回到activity所在进程时的调用流程时,把坑位Activity替换成真正要启动的Activity。(RePluginClassLoader.loadClass 完成)
3、插件化意味着类是动态加载的,系统默认创建的ClassLoader只能加载Apk中的类即宿主中的类,所以需要对ClassLoader进行操作使得它能加载插件中的类。(记录一中的PatchClassLoaderUtils.patch完成)
更多插件化相关知识可以看术哥的这篇文章,讲的很好 http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/ Android 插件化原理解析——Activity生命周期管理
PluginLibraryInternalProxy.startActivity
/**
* @hide 内部方法,插件框架使用
* 启动一个插件中的activity,如果插件不存在会触发下载界面
* @param context 应用上下文或者Activity上下文
* @param intent
* @param plugin 插件名
* @param activity 待启动的activity类名
* @param process 是否在指定进程中启动
* @param download 下载
* @return 插件机制层是否成功,例如没有插件存在、没有合适的Activity坑
*/
public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download) {
// 是否启动下载
// 若插件不可用(不存在或版本不匹配),则直接弹出“下载插件”对话框
// 因为已经打开UpdateActivity,故在这里返回True,告诉外界已经打开,无需处理
if (download) {
if (PluginTable.getPluginInfo(plugin) == null) {
// 如果用户在下载即将完成时突然点按“取消”,则有可能出现插件已下载成功,但没有及时加载进来的情况
// 因此我们会判断这种情况,如果是,则重新加载一次即可,反之则提示用户下载
// 原因:“取消”会触发Task.release方法,最终调用mDownloadTask.destroy,导致“下载服务”的Receiver被注销,即使文件下载了也没有回调回来
// NOTE isNeedToDownload方法会调用pluginDownloaded再次尝试加载
// 以下两种情况需要下载插件:
// 1、V5文件不存在(常见);
// 2、V5文件非法(加载失败)
if (isNeedToDownload(context, plugin)) {
return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
}
}
}
/* 检查是否是动态注册的类 */
// 如果要启动的 Activity 是动态注册的类,则不使用坑位机制,而是直接动态类。
// 所以如果动态类是四大组件,被替换的类需要在宿主中注册
// 原因:宿主的某些动态注册的类不能运行在坑位中(如'桌面'插件的入口Activity)
if (Factory2.isDynamicClass(plugin, activity)) {
intent.putExtra(IPluginManager.KEY_COMPATIBLE, true);
intent.setComponent(new ComponentName(IPC.getPackageName(), activity));
context.startActivity(intent);
return true;
}
// 如果插件状态出现问题,则每次弹此插件的Activity都应提示无法使用,或提示升级(如有新版)
// Added by Jiongxuan Zhang
if (PluginStatusController.getStatus(plugin) < PluginStatusController.STATUS_OK) {
return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
}
// 若为首次加载插件,且是“大插件”,则应异步加载,同时弹窗提示“加载中”
// Added by Jiongxuan Zhang
if (!RePlugin.isPluginDexExtracted(plugin)) {
PluginDesc pd = PluginDesc.get(plugin);
if (pd != null && pd.isLarge()) {
return RePlugin.getConfig().getCallbacks().onLoadLargePluginForActivity(context, plugin, intent, process);
}
}
// 缓存打开前的Intent对象,里面将包括Action等内容
Intent from = new Intent(intent);
// 帮助填写打开前的Intent的ComponentName信息(如有。没有的情况如直接通过Action打开等)
if (!TextUtils.isEmpty(plugin) && !TextUtils.isEmpty(activity)) {
from.setComponent(new ComponentName(plugin, activity));
}
//根据给要跳转的actvity所在插件找到一个合适的坑位
ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
// 将Intent指向到“坑位”。这样:
// from:插件原Intent
// to:坑位Intent
intent.setComponent(cn);
//启动坑位Activity,在RePluginClassLoader.loadClass加载Activity的时候会把它换回真正要加载的Activity
context.startActivity(intent);
// 通知外界,已准备好要打开Activity了
// 其中:from为要打开的插件的Intent,to为坑位Intent
RePlugin.getConfig().getEventCallbacks().onPrepareStartPitActivity(context, from, intent);
return true;
}
PluginCommImpl.loadPluginActivity
插件的坑位分配
public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {
ActivityInfo ai = null;
String container = null;
PluginBinderInfo info = new PluginBinderInfo(PluginBinderInfo.ACTIVITY_REQUEST);
// 获取 ActivityInfo
// 找到对应插件的activityInfo。前面在加载插件的时候会把mainfest的四大组件信息缓存下来
ai = getActivityInfo(plugin, activity, intent);
// 存储此 Activity 在插件 Manifest 中声明主题到 Intent,作为坑位选择的一个条件
intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
// 根据 activity 的 processName,选择进程 ID 标识
// 在Loader.loadDex加载插件的时候调用adjustPluginProcess(mPackageInfo.applicationInfo);
// 调整activity的目标进程。(根据组件的目标进程填写,判断是在UI进程,还是在常驻进程还是自定义进程
// 其中自定义进程需要把进程映射成p0, p1, p2这几个指定的进程)
// 注:这里即使是调整成常驻进程,在startPluginProcess 中无效,最后会由系统分配进程
if (ai.processName != null) {
process = PluginClientHelper.getProcessInt(ai.processName);
}
// 容器选择(启动目标进程),返回目标进程的一个binder客户端后续请求调用
IPluginClient client = MP.startPluginProcess(plugin, process, info);
// 目标进程远程分配坑位
container = client.allocActivityContainer(plugin, process, ai.name, intent);
//指向在宿主mainfest中注册好的组件坑位了
return new ComponentName(IPC.getPackageName(), container);
}
PmHostSvc.startPluginProcess
@Override
public IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException {
synchronized (this) {
return mPluginMgr.startPluginProcessLocked(plugin, process, info);
}
}
PmBase.startPluginProcessLocked
final IPluginClient startPluginProcessLocked(String plugin, int process, PluginBinderInfo info) {
// 如果是activity,binder这类请求,没有指定进程则强制使用UI进程
if (Constant.ENABLE_PLUGIN_ACTIVITY_AND_BINDER_RUN_IN_MAIN_UI_PROCESS) {
if (info.request == PluginBinderInfo.ACTIVITY_REQUEST) {
if (process == IPluginManager.PROCESS_AUTO) {
process = IPluginManager.PROCESS_UI;
}
}
if (info.request == PluginBinderInfo.BINDER_REQUEST) {
if (process == IPluginManager.PROCESS_AUTO) {
process = IPluginManager.PROCESS_UI;
}
}
}
//根据插件名称,指定进程从已创建的进程中获取,如果已存在且还存活则直接返回。
//对比PluginProcessMain.allocProcess来看
IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client != null) {
return client;
}
int index = IPluginManager.PROCESS_AUTO;
// 分配进程。三种情况,ui进程,自定义进程(需要在mainfest写meta-data
// 将自定义的进程映射成p0,p1,p2),由replugin来分配stub进程
//(不属于ui也不是自定义进程,不过看样子已经不用了,所以不考虑这种情况)
index = PluginProcessMain.allocProcess(plugin, process);
// 分配的坑位不属于UI、自定义进程和自动分配的进程,就返回。
if (!(index == IPluginManager.PROCESS_UI
|| PluginProcessHost.isCustomPluginProcess(index)
|| PluginManager.isPluginProcess(index))) {
return null;
}
//根据上面分配的进程index,组装成一个uri来启动预先在mainfest中埋好的provider,因为provider
// 在mainfest指定了目标进程,从而间接达到开启进程的效果。
boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
if (!rc) {
return null;
}
// 再次获取
client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client == null) {
return null;
}
return client;
}
自定义进程:
PluginProcessPer.allocActivityContainer
@Override
public String allocActivityContainer(String plugin, int process, String target, Intent intent) throws RemoteException {
// 一旦有分配,则进入监控状态(一是避免不退出的情况,二也是最重要的是避免现在就退出的情况)
RePlugin.getConfig().getEventCallbacks().onPrepareAllocPitActivity(intent);
String loadPlugin = null;
//,省略了process的调整,因为后面bindActivity其实没有用到
// 省略了plugin的判断从PluginCommImpl.loadPluginActivity过来的话因为插件的判断已经在前面判断过了
String container = bindActivity(loadPlugin, process, target, intent);
return container;
}
PluginProcessPer.bindActivity
final String bindActivity(String plugin, int process, String activity, Intent intent) {
/* 获取插件对象 */
Plugin p = mPluginMgr.loadAppPlugin(plugin);
/* 获取 ActivityInfo */
ActivityInfo ai = p.mLoader.mComponents.getActivity(activity);
/* 获取 Container */
String container;
// 根据是自定义进程还是UI进程,取不同的坑位信息。逻辑是一样的,所以只看一个mACM.alloc就够了
if (ai.processName.contains(PluginProcessHost.PROCESS_PLUGIN_SUFFIX2)) {
String processTail = PluginProcessHost.processTail(ai.processName);
container = mACM.alloc2(ai, plugin, activity, process, intent, processTail);
} else {
container = mACM.alloc(ai, plugin, activity, process, intent);
}
//检查 activity 是否存在。这个classLoader是PluginDexClassLoader,如果在插件找不到
// 会尝试在宿主中找该activity
c = p.mLoader.mClassLoader.loadClass(activity);
return container;
}
PluginContainers.alloc
final String alloc(ActivityInfo ai, String plugin, String activity, int process, Intent intent) {
ActivityState state;
String defaultPluginTaskAffinity = ai.applicationInfo.packageName;
//省略了其他启动模式,这里是/* SingleTask, SingleTop, Standard */
synchronized (mLock) {
state = allocLocked(ai, mLaunchModeStates.getStates(ai.launchMode, ai.theme), plugin, activity, intent);
}
return state.container;
}
PluginContainers.allocLocked
private final ActivityState allocLocked(ActivityInfo ai, HashMap map,
String plugin, String activity, Intent intent) {
// 这里就贴下代码,原本的注释也够清楚的了,state.occupy(plugin, activity)将坑位和插件目标activity关联起来
// 坑和状态的 map 为空
if (map == null) {
return null;
}
// 首先找上一个活的,或者已经注册的,避免多个坑到同一个activity的映射
for (ActivityState state : map.values()) {
if (state.isTarget(plugin, activity)) {
return state;
}
}
// 新分配:找空白的,第一个
for (ActivityState state : map.values()) {
if (state.state == STATE_NONE) {
state.occupy(plugin, activity);
return state;
}
}
ActivityState found;
// 重用:则找最老的那个
found = null;
for (ActivityState state : map.values()) {
if (!state.hasRef()) {
if (found == null) {
found = state;
} else if (state.timestamp < found.timestamp) {
found = state;
}
}
}
if (found != null) {
found.occupy(plugin, activity);
return found;
}
// 强挤:最后一招,挤掉:最老的那个
found = null;
for (ActivityState state : map.values()) {
if (found == null) {
found = state;
} else if (state.timestamp < found.timestamp) {
found = state;
}
}
if (found != null) {
found.finishRefs();
found.occupy(plugin, activity);
return found;
}
// never reach here
return null;
}
上面进入startActivity后,最后系统调回ActivityThread.performLaunchActivity来创建Activity实例
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
if (r.packageInfo == null) {
//获取LoadedApk,此时LoadedApk中的classLoader已经是RePluginClassLoader了
// 看记录一的流程PatchClassLoaderUtils.patch方法
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
//创建context的时候把classLoader设置为r.packageInfo.getClassLoader();
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
//最终调用cl.loadClass(className).newInstance()
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
}
RePluginClassLoader.loadClass
@Override
protected Class> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class> c = null;
//调用PmBase.loadClass 对应插件classLoader加载类或者是加载动态注册类
c = PMF.loadClass(className, resolve);
if (c != null) {
return c;
}
//
try {
//如果找不到就用默认系统生成的PathClassLoader即加载宿主的类
c = mOrig.loadClass(className);
return c;
} catch (Throwable e) {
//
}
//
return super.loadClass(className, resolve);
}
PmBase.loadClass
final Class> loadClass(String className, boolean resolve) {
//这里略去了contentprovider和service的情况
// 判断是否坑位Activity
if (mContainerActivities.contains(className)) {
Class> c = mClient.resolveActivityClass(className);
if (c != null) {
return c;
}
//找不到要加载的Activity返回DummyActivity,直接finish
return DummyActivity.class;
}
// 插件定制表
DynamicClass dc = mDynamicClasses.get(className);
if (dc != null) {
// 省略了加载失败的处理,其实也挺清晰的就是插件对应的classLoader去加载类
Plugin p = loadAppPlugin(dc.plugin);
if (p != null) {
try {
Class> cls = p.getClassLoader().loadClass(dc.className);
return cls;
} catch (Throwable e) {
}
}
//返回一个关闭自己的组件
// return dummy class
if ("activity".equals(dc.classType)) {
return DummyActivity.class;
} else if ("service".equals(dc.classType)) {
return DummyService.class;
} else if ("provider".equals(dc.classType)) {
return DummyProvider.class;
}
//如果不是组件,返回一个加载失败用的类
return dc.defClass;
}
//加载默认插件的类
return loadDefaultClass(className);
}
PluginProcessPer.resolveActivityClass
final Class> resolveActivityClass(String container) {
String plugin = null;
String activity = null;
// 根据container找之前bindActivity登记的ActivityState从而找到对应插件和activity,如果找不到,则用forward activity
PluginContainers.ActivityState state = mACM.lookupByContainer(container);
if (state == null) {
//ForwardActivity作用是当坑位出现丢失或错乱,则通过读取Intent.Category来做个中转,再尝试启动一次
return ForwardActivity.class;
}
plugin = state.plugin;
activity = state.activity;
Plugin p = mPluginMgr.loadAppPlugin(plugin);
ClassLoader cl = p.getClassLoader();
Class> c = null;
try {
//对应插件来加载activity类
c = cl.loadClass(activity);
} catch (Throwable e) {
}
return c;
}
参考:
Android 插件化原理解析——Activity生命周期管理:
http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/
RePlugin中如何打开插件中的自定义进程Activity:https://mp.weixin.qq.com/s/IpNcyTjML16og4LrxjxFmQ