RePlugin之插件Activity启动

Host中启动插件Activity

为什么同一配置要多个坑?同一坑多次启动不行?

启动插件Activity

开启一个占坑的插件Activity,需要使用RePlugin.startActivity来启动,为了避免Hook更多的API,所以无法在原生启动过程中(Instrumentation)动态替换,因此在进入原生启动流程之前,先做好准备工作和替换。

框架预处理

PluginLibraryInternalProxy.startActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download) {
...
// 是否启动下载
// 若插件不可用(不存在或版本不匹配),则直接弹出“下载插件”对话框
// 因为已经打开UpdateActivity,故在这里返回True,告诉外界已经打开,无需处理
if (download) {
// 如果在PluginTable.PLUGINS中找不到该插件,则尝试下载
if (PluginTable.getPluginInfo(plugin) == null) {
...
// 如果用户在下载即将完成时突然点按“取消”,则有可能出现插件已下载成功,但没有及时加载进来的情况
// 因此我们会判断这种情况,如果是,则重新加载一次即可,反之则提示用户下载
// 原因:“取消”会触发Task.release方法,最终调用mDownloadTask.destroy,导致“下载服务”的Receiver被注销,即使文件下载了也没有回调回来
// NOTE isNeedToDownload方法会调用pluginDownloaded再次尝试加载
// 如果插件文件存在,则不需要再次下载,直接加载。如果文件不存在,则需要下载
if (isNeedToDownload(context, plugin)) {
// 通知上层插件不存在
return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
}
}
}
...
// 如果插件状态出现问题,则每次弹此插件的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);
}
}

// WARNING:千万不要修改intent内容,尤其不要修改其ComponentName
// 因为一旦分配坑位有误(或压根不是插件Activity),则外界还需要原封不动的startActivity到系统中
// 可防止出现“本来要打开宿主,结果被改成插件”,进而无法打开宿主Activity的问题

// 缓存打开前的Intent对象,里面将包括Action等内容
Intent from = new Intent(intent);

// 帮助填写打开前的Intent的ComponentName信息(如有。没有的情况如直接通过Action打开等)
if (!TextUtils.isEmpty(plugin) && !TextUtils.isEmpty(activity)) {
from.setComponent(new ComponentName(plugin, activity));
}
// 分配坑位
ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
if (cn == null) {
// 坑位获取失败
return false;
}

// 将Intent指向到“坑位”。这样:
// from:插件原Intent
// to:坑位Intent
intent.setComponent(cn);
// 启动坑位Activity
context.startActivity(intent);
// 通知外界,已准备好要打开Activity了
// 其中:from为要打开的插件的Intent,to为坑位Intent
RePlugin.getConfig().getEventCallbacks().onPrepareStartPitActivity(context, from, intent);
return true;
}
分配坑位Activity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {

ActivityInfo ai = null;
String container = null;
PluginBinderInfo info = new PluginBinderInfo(PluginBinderInfo.ACTIVITY_REQUEST);

try {
// 获取 ActivityInfo(可能是其它插件的 Activity,所以这里使用 pair 将 pluginName 也返回)
ai = getActivityInfo(plugin, activity, intent);
if (ai == null) {
// 从插件的PackageInfo中找不到该插件Activity的ActivityInfo
return null;
}
// 存储此 Activity 在插件 Manifest 中声明主题到 Intent
intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
...
// 根据 activity 的 processName,选择进程 ID 标识
if (ai.processName != null) {
process = PluginClientHelper.getProcessInt(ai.processName);
}
// 容器选择(启动目标进程)
IPluginClient client = MP.startPluginProcess(plugin, process, info);
if (client == null) {
return null;
}

// 插件所在进程远程分配坑位
container = client.allocActivityContainer(plugin, process, ai.name, intent);
...
} catch (Throwable e) {
...
}
// 分配失败
if (TextUtils.isEmpty(container)) {
return null;
}
PmBase.cleanIntentPluginParams(intent);
...
// 就坑位Activity信息返回
return new ComponentName(IPC.getPackageName(), container);
}
获取插件Activity的ActivityInfo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public ActivityInfo getActivityInfo(String plugin, String activity, Intent intent) {
// 获取插件对象
Plugin p = mPluginMgr.loadAppPlugin(plugin);
if (p == null) {
// 获取插件对象失败
return null;
}

ActivityInfo ai = null;
// activity 不为空时,从插件声明的 Activity 集合中查找
if (!TextUtils.isEmpty(activity)) {
// Plugin.mLoader.mComponents中保存了该插件所有的组件列表,该组件信息从插件的PackageInfo中获取
ai = p.mLoader.mComponents.getActivity(activity);
} else {
// activity 为空时,根据 Intent 匹配
ai = IntentMatcherHelper.getActivityInfo(mContext, plugin, intent);
}
return ai;
}
解压插件

在doLoad方法中,会将插件包解压,将dex和so文件提取出来,通过应用的PackageManager加载该插件apk,然后获取该插件的PackageInfo。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
private boolean loadLocked(int load, boolean useCache) {
// 若插件被“禁用”,则即便上次加载过(且进程一直活着),这次也不能再次使用了
// Added by Jiongxuan Zhang
int status = PluginStatusController.getStatus(mInfo.getName(), mInfo.getVersion());
if (status < PluginStatusController.STATUS_OK) {
// 加载失败
return false;
}
if (mInitialized) {
if (mLoader == null) {
return false;
}
if (load == LOAD_INFO) {
boolean rl = mLoader.isPackageInfoLoaded();
return rl;
}
if (load == LOAD_RESOURCES) {
boolean rl = mLoader.isResourcesLoaded();
return rl;
}
if (load == LOAD_DEX) {
boolean rl = mLoader.isDexLoaded();
return rl;
}
boolean il = mLoader.isAppLoaded();
// 如果已经初始化过该插件,则直接返回是否被加载
return il;
}
mInitialized = true;
...
// 这里先处理一下,如果cache命中,省了后面插件提取(如释放Jar包等)操作
if (useCache) {
boolean result = loadByCache(load);
// 如果缓存命中,则直接返回
if (result) {
return true;
}
}
Context context = mContext;
ClassLoader parent = mParent;
PluginCommImpl manager = mPluginManager;
// 第一次尝试加载
boolean rc = doLoad(logTag, context, parent, manager, load);
if (rc) {
try {
// 第一次加载成功,至此,该插件已开始运行
PluginManagerProxy.addToRunningPluginsNoThrows(mInfo.getName());
} catch (Throwable e) {
...
}
return true;
}
...
// 清除第一次加载失败的残留
// 清空数据对象
mLoader = null;
// 删除优化dex文件
File odex = mInfo.getDexFile();
if (odex.exists()) {
odex.delete();
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// support for multidex below LOLLIPOP:delete Extra odex,if need
try {
FileUtils.forceDelete(mInfo.getExtraOdexDir());
} catch (IOException e) {
e.printStackTrace();
}
}
// 尝试第二次加载
rc = doLoad(logTag, context, parent, manager, load);
if (!rc) {
// 第二次失败则直接返回失败
return false;
}
...
try {
// 第二次成功,至此,该插件已开始运行
PluginManagerProxy.addToRunningPluginsNoThrows(mInfo.getName());
} catch (Throwable e) {
...
}
return true;
}
处理dex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 final boolean loadDex(ClassLoader parent, int load) {
try {
PackageManager pm = mContext.getPackageManager();

mPackageInfo = Plugin.queryCachedPackageInfo(mPath);
if (mPackageInfo == null) {
// 通过应用的PackageManager加载插件APK,获得插件的PackageInfo
// getPackageArchiveInfo可以获取没有安装(没有在PMS中注册的)的应用的包信息
mPackageInfo = pm.getPackageArchiveInfo(mPath,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_META_DATA);
if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
mPackageInfo = null;
return false;
}
// 将插件包信息中的资源路径(此处修改applicationInfo资源路径后,就可以通过pm.getResourcesForApplication方法获取资源Resources对象)和so路径指向之前解压的路径
mPackageInfo.applicationInfo.sourceDir = mPath;
mPackageInfo.applicationInfo.publicSourceDir = mPath;
if (TextUtils.isEmpty(mPackageInfo.applicationInfo.processName)) {
mPackageInfo.applicationInfo.processName = mPackageInfo.applicationInfo.packageName;
}
...
mPackageInfo.applicationInfo.nativeLibraryDir = ld.getAbsolutePath();
}
...
// 创建或获取ComponentList表
// Added by Jiongxuan Zhang
mComponents = Plugin.queryCachedComponentList(mPath);
if (mComponents == null) {
// ComponentList
mComponents = new ComponentList(mPackageInfo, mPath, mPluginObj.mInfo);

// 动态注册插件中声明的 receiver
// 将插件manifest中静态注册的receiver通过常驻进程动态注册,虽然无法实现静态注册,但是由于常驻进程存活时间长,所以即使插件进程退出,也可以接受到通知。
regReceivers();

// 缓存表:ComponentList
// 将PackageInfo中的组件都缓存到列表中,方便查询
synchronized (Plugin.FILENAME_2_COMPONENT_LIST) {
Plugin.FILENAME_2_COMPONENT_LIST.put(mPath, new WeakReference<>(mComponents));
}
/* 只调整一次 */
// 调整插件中组件的进程名称
adjustPluginProcess(mPackageInfo.applicationInfo);
// 调整插件中 Activity 的 TaskAffinity
adjustPluginTaskAffinity(mPluginName, mPackageInfo.applicationInfo);
}
if (load == Plugin.LOAD_INFO) {
return isPackageInfoLoaded();
}
mPkgResources = Plugin.queryCachedResources(mPath);
// LOAD_RESOURCES和LOAD_ALL都会获取资源,但LOAD_INFO不可以(只允许获取PackageInfo)
if (mPkgResources == null) {
// Resources
try {
if (BuildConfig.DEBUG) {
// 如果是Debug模式的话,防止与Instant Run冲突,资源重新New一个
Resources r = pm.getResourcesForApplication(mPackageInfo.applicationInfo);
mPkgResources = new Resources(r.getAssets(), r.getDisplayMetrics(), r.getConfiguration());
} else {
// 通过上面获得的PackageInfo和对applicationInfo内资源路径的修改,得到一个指向之前解压的资源的Resources对象
mPkgResources = pm.getResourcesForApplication(mPackageInfo.applicationInfo);
}
} catch (NameNotFoundException e) {
return false;
}
if (mPkgResources == null) {
return false;
}
// 缓存表: Resources
synchronized (Plugin.FILENAME_2_RESOURCES) {
Plugin.FILENAME_2_RESOURCES.put(mPath, new WeakReference<>(mPkgResources));
}
}
if (load == Plugin.LOAD_RESOURCES) {
return isResourcesLoaded();
}

mClassLoader = Plugin.queryCachedClassLoader(mPath);
if (mClassLoader == null) {
// ClassLoader
String out = mPluginObj.mInfo.getDexParentDir().getPath();
//changeDexMode(out);

//
Log.i("dex", "load " + mPath + " ...");
if (BuildConfig.DEBUG) {
// 因为Instant Run会替换parent为IncrementalClassLoader,所以在DEBUG环境里
// 需要替换为BootClassLoader才行
// Added by yangchao-xy & Jiongxuan Zhang
parent = ClassLoader.getSystemClassLoader();
} else {
// 线上环境保持不变
parent = getClass().getClassLoader().getParent(); // TODO: 这里直接用父类加载器
}
String soDir = mPackageInfo.applicationInfo.nativeLibraryDir;

long begin = 0;
boolean isDexExist = false;
// 通过解压的dex路径和so路径,创建一个PluginDexClassLoader,用来加载该插件的class
mClassLoader = RePlugin.getConfig().getCallbacks().createPluginClassLoader(mPluginObj.mInfo, mPath, out, soDir, parent);

if (mClassLoader == null) {
return false;
}
// 缓存表:ClassLoader
synchronized (Plugin.FILENAME_2_DEX) {
Plugin.FILENAME_2_DEX.put(mPath, new WeakReference<>(mClassLoader));
}
}
if (load == Plugin.LOAD_DEX) {
return isDexLoaded();
}

// 创建插件的上下文对象
mPkgContext = new PluginContext(mContext, android.R.style.Theme, mClassLoader, mPkgResources, mPluginName, this);

} catch (Throwable e) {
return false;
}

return true;
}
创建插件Application

安装完插件后,会检查插件是否需要创建插件application,如果需要,则创建插件的application,并调用其attachBaseContext和onCreate回调,并将上面创建的PluginContext作为mBase。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 private void callAppLocked() {
// 获取并调用Application的几个核心方法
if (!mDummyPlugin) {
// NOTE 不排除A的Application中调到了B,B又调回到A,或在同一插件内的onCreate开启Service/Activity,而内部逻辑又调用fetchContext并再次走到这里
// NOTE 因此需要对mApplicationClient做判断,确保永远只执行一次,无论是否成功
if (mApplicationClient != null) {
// 已经初始化过,无需再次处理
return;
}
// 通过反射创建插件manifest中配置的application
mApplicationClient = PluginApplicationClient.getOrCreate(
mInfo.getName(), mLoader.mClassLoader, mLoader.mComponents, mLoader.mPluginObj.mInfo);
// 将PluginContext作为参数传给application
if (mApplicationClient != null) {
mApplicationClient.callAttachBaseContext(mLoader.mPkgContext);
mApplicationClient.callOnCreate();
}
} else {
}
}
启动目标进程

回到第3步,拿到了已经安装好的插件对象,从插件的组件列表(Plugin.mLoader.mComponents)中获得启动的Activity的信息,通过该Activity的process信息和插件信息从常驻进程服务那获得该插件的PluginClient服务对象,通过该对象和插件进程进行通信,如果插件进程没有启动,则通过provider启动该进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  final IPluginClient startPluginProcessLocked(String plugin, int process, PluginBinderInfo info) {
...
//
PluginProcessMain.schedulePluginProcessLoop(PluginProcessMain.CHECK_STAGE1_DELAY);

// 获取插件进程在常驻进程中注册的服务代理对象
IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client != null) {
return client;
}

// 分配,如果插件进程没有启动
int index = IPluginManager.PROCESS_AUTO;
try {
index = PluginProcessMain.allocProcess(plugin, process);
} catch (Throwable e) {
}
// 分配的坑位不属于UI、和自定义进程,就返回。
if (!(index == IPluginManager.PROCESS_UI
|| PluginProcessHost.isCustomPluginProcess(index)
|| PluginManager.isPluginProcess(index))) {
return null;
}

// 启动,通过provider启动插件进程
boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
if (!rc) {
return null;
}

// 再次获取该插件进程注册的服务代理
client = PluginProcessMain.probePluginClient(plugin, process, info);
if (client == null) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "spp pc n");
}
return null;
}
return client;
}
目标进程分配坑位

根据启动Activity的启动模式和样式从注册的坑位中分配一个合适的坑位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
final String bindActivity(String plugin, int process, String activity, Intent intent) {

/* 获取插件对象 */
Plugin p = mPluginMgr.loadAppPlugin(plugin);
if (p == null) {
return null;
}

/* 获取 ActivityInfo */
ActivityInfo ai = p.mLoader.mComponents.getActivity(activity);
if (ai == null) {
return null;
}

if (ai.processName == null) {
ai.processName = ai.applicationInfo.processName;
}
if (ai.processName == null) {
ai.processName = ai.packageName;
}

/* 获取 Container */
String container;

// 自定义进程
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);
}

if (TextUtils.isEmpty(container)) {
return null;
}

/* 检查 activity 是否存在 */
Class<?> c = null;
try {
c = p.mLoader.mClassLoader.loadClass(activity);
} catch (Throwable e) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, e.getMessage(), e);
}
}
if (c == null) {
return null;
}

return container;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
private final ActivityState allocLocked(ActivityInfo ai, HashMap<String, ActivityState> map,
String plugin, String activity, Intent intent) {
// 坑和状态的 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;
}
启动坑位Activity

回到第2步中,在获取到坑位Activity后,直接将其放到Intent中,然后正常启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
if (cn == null) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "plugin cn not found: intent=" + intent + " plugin=" + plugin + " activity=" + activity + " process=" + process);
}
return false;
}

// 将Intent指向到“坑位”。这样:
// from:插件原Intent
// to:坑位Intent
intent.setComponent(cn);

if (LOG) {
LogDebug.d(PLUGIN_TAG, "start activity: real intent=" + intent);
}

// if (RePluginInternal.FOR_DEV) {
// try {
// String str = cn.getPackageName() + "/" + cn.getClassName();
// if (LOG) {
// LogDebug.d(PLUGIN_TAG, "str=" + str);
// }
// new ProcessBuilder().command("am", "start", "-D", "--user", "0", "-n", str).start();
// } catch (IOException e) {
// e.printStackTrace();
// }
// } else {

context.startActivity(intent);
ClassLoader替换坑位

坑位Activity被正常启动,经过AMS和当前进程以及目标进程多次来回后,在performLaunchActivity时,使用CLassLoader来加载该Activity类。使用的是appContext.getClassLoader,而ContextImpl中的classLoader是从PackageInfo中获取的,在上一篇中提到,唯一的Hook点就是在启动的时候将PackageInfo.mClassLoader改成RePluginClassLoader对象,所以最终会进入RePluginClassLoader.loadClass方法。在该方法中会使用插件的PluginDexClassLoader来加载坑位对应的Activity,然后返回到performLaunchActivity中继续启动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {

}
...
return activity;
}
加载真正的插件Activity

在RepluginClassLoader的loadClass方法中,会先通过PMF.loadClass加载类,如果失败,则由原生的PathDexClassLoader来加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
final Class<?> loadClass(String className, boolean resolve) {
// 加载Service中介坑位
if (className.startsWith(PluginPitService.class.getName())) {
return PluginPitService.class;
}

// 如果Activity坑位列表中包含该类,则尝试将坑位替换成原本的Activity
if (mContainerActivities.contains(className)) {
Class<?> c = mClient.resolveActivityClass(className);
if (c != null) {
// 返回插件Activity
return c;
}
return DummyActivity.class;
}

// 如果Service坑位列表中包含该类,则尝试将坑位替换成原本的Service
if (mContainerServices.contains(className)) {
Class<?> c = loadServiceClass(className);
if (c != null) {
return c;
}
return DummyService.class;
}

// 如果Provider坑位列表中包含该类,则尝试将坑位替换成原本的Provider
if (mContainerProviders.contains(className)) {
Class<?> c = loadProviderClass(className);
if (c != null) {
return c;
}
// 输出warn日志便于查看
// use DummyProvider orig=
if (LOGR) {
LogRelease.w(PLUGIN_TAG, "p m hlc u d p o " + className);
}
return DummyProvider.class;
}
return loadDefaultClass(className);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  final Class<?> resolveActivityClass(String container) {
String plugin = null;
String activity = null;

// 先找登记的,如果找不到,则用forward activity
PluginContainers.ActivityState state = mACM.lookupByContainer(container);
if (state == null) {
return ForwardActivity.class;
}
plugin = state.plugin;
activity = state.activity;

Plugin p = mPluginMgr.loadAppPlugin(plugin);
if (p == null) {
return null;
}
// 获取插件的ClassLoader
ClassLoader cl = p.getClassLoader();
Class<?> c = null;
try {
// 使用插件的PluginDexClassLoader来加载插件Activity
c = cl.loadClass(activity);
} catch (Throwable e) {
}
return c;
}
PluginActivity替换上下文

实际上插件的每个Activity在编译的时候都会改成继承RePlugin自己定义的Activity(这些都定义在plugin-lib中),该Activity会在声明周期触发时,通过反射调用host-lib进行一系列的操作。在attach中会将Activity的上下文改成PluginContext,这样当我们使用context.getResources等方法时,得到的就是插件的resources。

1
2
3
4
5
6
7
8
public abstract class PluginActivity extends Activity {
@Override
protected void attachBaseContext(Context newBase) {
// 通过反射创建PluginContext,这样插件Activity的上下文就是PluginContext,其内部的ClassLoader和Resources就是该插件的ClassLoader和Resources。
newBase = RePluginInternal.createActivityContext(this, newBase);
super.attachBaseContext(newBase);
}
}
1
2
3
final Context createBaseContext(Context newBase) {
return new PluginContext(newBase, android.R.style.Theme, mClassLoader, mPkgResources, mPluginName, this);
}

流程如下:

graph TD
A[启动Activity_A] --> B{插件是否下载}
B-->|否|C[通知上层插件不存在]
B-->|是|D{插件是否安装}
D-->|否|E[解压插件,处理dex,获得PackageInfo]
D-->|是|F[获取A的ActivityInfo]
E-->F
F-->G[启动目标进程,并获取目标进程Binder服务代理]
G-->H[通知目标进程服务分配坑位,获得坑位Activity]
H-->I[启动坑位Activity]
I-->J[AMS正常启动坑位Activity]
J-->K[目标进程newActivity]
K-->L[RePluginClassLoader将坑位替换]
L-->M[使用目标插件的PluginDexClassLoader加载真正的Activity_A]
M-->N[拿到替换的Activity对象继续和AMS交互]
N-->O[attach时替换Activity的上下文]

RePlugin-分析计划

  1. RePlugin之Host进程启动
  2. RePlugin之插件Activity启动

RePlugin之Host进程启动

常驻进程

RePlugin默认开启常驻进程,所谓常驻进程就是单独启动一个进程来运行插件服务,可以在gradle中配置关闭常驻进程,那么服务会运行在UI进程。

1
2
3
4
repluginHostConfig {
/** 是否使用常驻进程? */
persistentEnable false
}

启用常驻进程下插件服务的启动

UI进程启动

创建RePluginApplication,在attachBaseContext函数中开始初始化RePlugin框架。

初始化RePlugin

通过RePlugin.App.attachBaseContext函数初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void attachBaseContext(Application app, RePluginConfig config) {
...
// 标记当前进程是UI进程还是常驻进程
IPC.init(app);
...
// 通过反射获取gradle中repluginHostConfig中的配置信息
HostConfigHelper.init();
...
// 创建PmBase对象,初始化插件管理框架,并且Hook应用的ClassLoader
PMF.init(app);
// 挂载所有插件
PMF.callAttach();

sAttached = true;
}

创建PmBase对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PmBase(Context context) {
mContext = context;
// 添加客户端进程对应的provider和service的authorities,用来启动对应的组件
// processIndex是子进程个数,不能超过2
if (PluginManager.sPluginProcessIndex == IPluginManager.PROCESS_UI || PluginManager.isPluginProcess()) {
String suffix;
if (PluginManager.sPluginProcessIndex == IPluginManager.PROCESS_UI) {
suffix = "N1";
} else {
suffix = "" + PluginManager.sPluginProcessIndex;
}
// 如果是UI进程则启动ProviderN1和ServiceN1
// 如果是插件进程则启动Provider0、Provider1和Service0、Service1
mContainerProviders.add(IPC.getPackageName() + CONTAINER_PROVIDER_PART + suffix);
mContainerServices.add(IPC.getPackageName() + CONTAINER_SERVICE_PART + suffix);
}

// 4.创建Binder服务PluginClient,每个客户端都拥有一个该服务,常驻进程持有所有客户端进程该服务的代理对象,
// 常驻进程通过mClient服务的代理调用客户端的PluginProcessPer服务
mClient = new PluginProcessPer(context, this, PluginManager.sPluginProcessIndex, mContainerActivities);

//
mLocal = new PluginCommImpl(context, this);

//
mInternal = new PluginLibraryInternalProxy(this);
}
创建PluginClient服务

每个客户端都有一个PluginClient服务,该服务包含PluginServiceServer子服务和一个坑管理容器,因此每个客户端进程都有一个自己的坑容器。

1
2
3
4
5
6
7
8
9
10
11
12
PluginProcessPer(Context context, PmBase pm, int process, HashSet<String> containers) {
mContext = context;
mPluginMgr = pm;
// 在服务中又创建了子服务 PluginServiceServer
mServiceMgr = new PluginServiceServer(context);

// 创建插件容器管理,坑的分配
mACM = new PluginContainers();
// UI进程和自定义子进程初始化给定启动模式的activity对应manifest中占的坑的名称
// 也就是设置坑位
mACM.init(process, containers);
}

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×