通過Replugin 記錄(二)插件啟動過程,我們知道插件中的Activity被gradle修改為繼承PluginActivity,其中startActivty被重寫,通過Factory2.startActivity顯式調(diào)用Replugin啟動插件activity的方法
public void startActivity(Intent intent) {
//反射調(diào)用Factory2.startActivity
if (RePluginInternal.startActivity(this, intent)) {
// 這個地方不需要回調(diào)startActivityAfter染苛,因為Factory2最終還是會回調(diào)回來沙热,最終還是要走super.startActivity()
return;
}
super.startActivity(intent);
}
除此之外還可以通過RePlugin.startActivity()在宿主啟動插件activity袭景。這兩個方法最終都會調(diào)用Factory類的startActivityWithNoInjectCN酝豪。
/**
* 內(nèi)部接口敛惊,僅為Factory2.startActivity(context, intent) 和 RePlugin.startActivity方法而使用
*
* @param context 應(yīng)用上下文或者Activity上下文
* @param intent Intent對象
* @param plugin 插件名
* @param activity 待啟動的activity類名
* @param process 是否在指定進(jìn)程中啟動
* @return 插件機(jī)制層膊毁,是否成功胀莹,例如沒有插件存在、沒有合適的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媚媒,如果插件不存在會觸發(fā)下載界面
* @param context 應(yīng)用上下文或者Activity上下文
* @param intent
* @param plugin 插件名
* @param activity 待啟動的activity類名
* @param process 是否在指定進(jìn)程中啟動
* @return 插件機(jī)制層是否成功,例如沒有插件存在涩僻、沒有合適的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逆日、把要啟動的目標(biāo)Activity替換成注冊在mainfest上的占坑Activity(其中涉及進(jìn)程選擇啟動嵌巷,坑位選擇分配)
而替換的原因是系統(tǒng)在startActivity的過程中會根據(jù)intent去PackageManagerService查詢我們要啟動的activity是否已經(jīng)在mainfest中注冊了。(PluginLibraryInternalProxy.startActivity)
2室抽、在startActivity流程從ActivityManagerService回到activity所在進(jìn)程時的調(diào)用流程時搪哪,把坑位Activity替換成真正要啟動的Activity。(RePluginClassLoader.loadClass 完成)
3坪圾、插件化意味著類是動態(tài)加載的晓折,系統(tǒng)默認(rèn)創(chuàng)建的ClassLoader只能加載Apk中的類即宿主中的類惑朦,所以需要對ClassLoader進(jìn)行操作使得它能加載插件中的類。(記錄一中的PatchClassLoaderUtils.patch完成)
更多插件化相關(guān)知識可以看術(shù)哥的這篇文章漓概,講的很好 http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/ Android 插件化原理解析——Activity生命周期管理
PluginLibraryInternalProxy.startActivity
/**
* @hide 內(nèi)部方法漾月,插件框架使用
* 啟動一個插件中的activity,如果插件不存在會觸發(fā)下載界面
* @param context 應(yīng)用上下文或者Activity上下文
* @param intent
* @param plugin 插件名
* @param activity 待啟動的activity類名
* @param process 是否在指定進(jìn)程中啟動
* @param download 下載
* @return 插件機(jī)制層是否成功胃珍,例如沒有插件存在梁肿、沒有合適的Activity坑
*/
public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download) {
// 是否啟動下載
// 若插件不可用(不存在或版本不匹配),則直接彈出“下載插件”對話框
// 因為已經(jīng)打開UpdateActivity觅彰,故在這里返回True吩蔑,告訴外界已經(jīng)打開,無需處理
if (download) {
if (PluginTable.getPluginInfo(plugin) == null) {
// 如果用戶在下載即將完成時突然點(diǎn)按“取消”填抬,則有可能出現(xiàn)插件已下載成功烛芬,但沒有及時加載進(jìn)來的情況
// 因此我們會判斷這種情況,如果是痴奏,則重新加載一次即可蛀骇,反之則提示用戶下載
// 原因:“取消”會觸發(fā)Task.release方法,最終調(diào)用mDownloadTask.destroy读拆,導(dǎo)致“下載服務(wù)”的Receiver被注銷擅憔,即使文件下載了也沒有回調(diào)回來
// NOTE isNeedToDownload方法會調(diào)用pluginDownloaded再次嘗試加載
// 以下兩種情況需要下載插件:
// 1、V5文件不存在(常見)檐晕;
// 2暑诸、V5文件非法(加載失敗)
if (isNeedToDownload(context, plugin)) {
return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
}
}
}
/* 檢查是否是動態(tài)注冊的類 */
// 如果要啟動的 Activity 是動態(tài)注冊的類辟灰,則不使用坑位機(jī)制个榕,而是直接動態(tài)類。
// 所以如果動態(tài)類是四大組件芥喇,被替換的類需要在宿主中注冊
// 原因:宿主的某些動態(tài)注冊的類不能運(yùn)行在坑位中(如'桌面'插件的入口Activity)
if (Factory2.isDynamicClass(plugin, activity)) {
intent.putExtra(IPluginManager.KEY_COMPATIBLE, true);
intent.setComponent(new ComponentName(IPC.getPackageName(), activity));
context.startActivity(intent);
return true;
}
// 如果插件狀態(tài)出現(xiàn)問題西采,則每次彈此插件的Activity都應(yīng)提示無法使用,或提示升級(如有新版)
// Added by Jiongxuan Zhang
if (PluginStatusController.getStatus(plugin) < PluginStatusController.STATUS_OK) {
return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
}
// 若為首次加載插件继控,且是“大插件”械馆,則應(yīng)異步加載,同時彈窗提示“加載中”
// 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等內(nèi)容
Intent from = new Intent(intent);
// 幫助填寫打開前的Intent的ComponentName信息(如有霹崎。沒有的情況如直接通過Action打開等)
if (!TextUtils.isEmpty(plugin) && !TextUtils.isEmpty(activity)) {
from.setComponent(new ComponentName(plugin, activity));
}
//根據(jù)給要跳轉(zhuǎn)的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);
// 通知外界尾菇,已準(zhǔn)備好要打開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
// 找到對應(yīng)插件的activityInfo。前面在加載插件的時候會把mainfest的四大組件信息緩存下來
ai = getActivityInfo(plugin, activity, intent);
// 存儲此 Activity 在插件 Manifest 中聲明主題到 Intent派诬,作為坑位選擇的一個條件
intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
// 根據(jù) activity 的 processName劳淆,選擇進(jìn)程 ID 標(biāo)識
// 在Loader.loadDex加載插件的時候調(diào)用adjustPluginProcess(mPackageInfo.applicationInfo);
// 調(diào)整activity的目標(biāo)進(jìn)程。(根據(jù)組件的目標(biāo)進(jìn)程填寫千埃,判斷是在UI進(jìn)程憔儿,還是在常駐進(jìn)程還是自定義進(jìn)程
// 其中自定義進(jìn)程需要把進(jìn)程映射成p0, p1, p2這幾個指定的進(jìn)程)
// 注:這里即使是調(diào)整成常駐進(jìn)程,在startPluginProcess 中無效放可,最后會由系統(tǒng)分配進(jìn)程
if (ai.processName != null) {
process = PluginClientHelper.getProcessInt(ai.processName);
}
// 容器選擇(啟動目標(biāo)進(jìn)程)谒臼,返回目標(biāo)進(jìn)程的一個binder客戶端后續(xù)請求調(diào)用
IPluginClient client = MP.startPluginProcess(plugin, process, info);
// 目標(biāo)進(jìn)程遠(yuǎn)程分配坑位
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這類請求耀里,沒有指定進(jìn)程則強(qiáng)制使用UI進(jìn)程
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;
}
}
}
//根據(jù)插件名稱蜈缤,指定進(jìn)程從已創(chuàng)建的進(jìn)程中獲取,如果已存在且還存活則直接返回冯挎。
//對比PluginProcessMain.allocProcess來看
IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client != null) {
return client;
}
int index = IPluginManager.PROCESS_AUTO;
// 分配進(jìn)程底哥。三種情況,ui進(jìn)程房官,自定義進(jìn)程(需要在mainfest寫meta-data
// 將自定義的進(jìn)程映射成p0,p1,p2)趾徽,由replugin來分配stub進(jìn)程
//(不屬于ui也不是自定義進(jìn)程,不過看樣子已經(jīng)不用了,所以不考慮這種情況)
index = PluginProcessMain.allocProcess(plugin, process);
// 分配的坑位不屬于UI翰守、自定義進(jìn)程和自動分配的進(jìn)程孵奶,就返回。
if (!(index == IPluginManager.PROCESS_UI
|| PluginProcessHost.isCustomPluginProcess(index)
|| PluginManager.isPluginProcess(index))) {
return null;
}
//根據(jù)上面分配的進(jìn)程index,組裝成一個uri來啟動預(yù)先在mainfest中埋好的provider蜡峰,因為provider
// 在mainfest指定了目標(biāo)進(jìn)程了袁,從而間接達(dá)到開啟進(jìn)程的效果畦韭。
boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
if (!rc) {
return null;
}
// 再次獲取
client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client == null) {
return null;
}
return client;
}
自定義進(jìn)程:
PluginProcessPer.allocActivityContainer
@Override
public String allocActivityContainer(String plugin, int process, String target, Intent intent) throws RemoteException {
// 一旦有分配谋币,則進(jìn)入監(jiān)控狀態(tài)(一是避免不退出的情況,二也是最重要的是避免現(xiàn)在就退出的情況)
RePlugin.getConfig().getEventCallbacks().onPrepareAllocPitActivity(intent);
String loadPlugin = null;
//辙培,省略了process的調(diào)整油航,因為后面bindActivity其實沒有用到
// 省略了plugin的判斷從PluginCommImpl.loadPluginActivity過來的話因為插件的判斷已經(jīng)在前面判斷過了
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;
// 根據(jù)是自定義進(jìn)程還是UI進(jìn)程崭庸,取不同的坑位信息。邏輯是一樣的谊囚,所以只看一個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<String, ActivityState> map,
String plugin, String activity, Intent intent) {
// 這里就貼下代碼熬粗,原本的注釋也夠清楚的了,state.occupy(plugin, activity)將坑位和插件目標(biāo)activity關(guān)聯(lián)起來
// 坑和狀態(tài)的 map 為空
if (map == null) {
return null;
}
// 首先找上一個活的搀玖,或者已經(jīng)注冊的余境,避免多個坑到同一個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;
}
// 強(qiáng)擠:最后一招,擠掉:最老的那個
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;
}
上面進(jìn)入startActivity后芳来,最后系統(tǒng)調(diào)回ActivityThread.performLaunchActivity來創(chuàng)建Activity實例
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
if (r.packageInfo == null) {
//獲取LoadedApk含末,此時LoadedApk中的classLoader已經(jīng)是RePluginClassLoader了
// 看記錄一的流程PatchClassLoaderUtils.patch方法
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
//創(chuàng)建context的時候把classLoader設(shè)置為r.packageInfo.getClassLoader();
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
//最終調(diào)用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;
//調(diào)用PmBase.loadClass 對應(yīng)插件classLoader加載類或者是加載動態(tài)注冊類
c = PMF.loadClass(className, resolve);
if (c != null) {
return c;
}
//
try {
//如果找不到就用默認(rèn)系統(tǒng)生成的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) {
// 省略了加載失敗的處理即舌,其實也挺清晰的就是插件對應(yīng)的classLoader去加載類
Plugin p = loadAppPlugin(dc.plugin);
if (p != null) {
try {
Class<?> cls = p.getClassLoader().loadClass(dc.className);
return cls;
} catch (Throwable e) {
}
}
//返回一個關(guān)閉自己的組件
// 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;
}
//加載默認(rèn)插件的類
return loadDefaultClass(className);
}
PluginProcessPer.resolveActivityClass
final Class<?> resolveActivityClass(String container) {
String plugin = null;
String activity = null;
// 根據(jù)container找之前bindActivity登記的ActivityState從而找到對應(yīng)插件和activity,如果找不到顽聂,則用forward activity
PluginContainers.ActivityState state = mACM.lookupByContainer(container);
if (state == null) {
//ForwardActivity作用是當(dāng)坑位出現(xiàn)丟失或錯亂肥惭,則通過讀取Intent.Category來做個中轉(zhuǎn),再嘗試啟動一次
return ForwardActivity.class;
}
plugin = state.plugin;
activity = state.activity;
Plugin p = mPluginMgr.loadAppPlugin(plugin);
ClassLoader cl = p.getClassLoader();
Class<?> c = null;
try {
//對應(yīng)插件來加載activity類
c = cl.loadClass(activity);
} catch (Throwable e) {
}
return c;
}
參考:
Android 插件化原理解析——Activity生命周期管理:
http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/
RePlugin中如何打開插件中的自定義進(jìn)程Activity:https://mp.weixin.qq.com/s/IpNcyTjML16og4LrxjxFmQ