RePlugin解析startService流程分析

引言

RePlugin解析-startActivity流程分析一文中,详细分析了插件的加载,进程的启动以及Activity坑位的分配等内容,由于Activity不仅涉及到process和taskAffinity属性,还涉及到launchMode属性,所以startActivity流程无疑是最复杂的。然后,startService的流程和startActivity还是有些不一样的,所以考虑再三,觉得还是有必要写一篇文章专门分析一下startService的流程

1.startService入口

普通App中的Activity调用startService()时,实际上调用的是ContextWrapper中的mBase的startService()方法,而mBase是ContextImpl对象,那么对于RePlugin中的PluginActivity,调用startService()时,还是调用ContextImpl的startService()方法吗?

回到PluginActivity的attachBaseContext()方法就知道了:

1
2
3
4
5
@Override
protected void attachBaseContext(Context newBase) {
newBase = RePluginInternal.createActivityContext(this, newBase);
super.attachBaseContext(newBase);
}

注意这里调用super.attachBaseContext()就会用newBase给ContextWrapper的mBase赋值。下面看一下newBase到底是什么吧。

RePluginInternal.createActivityContext()方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @param activity
* @param newBase
* @return 为Activity构造一个base Context
* @hide 内部方法,插件框架使用
* 插件的Activity创建成功后通过此方法获取其base context
*/
public static Context createActivityContext(Activity activity, Context newBase) {
if (!RePluginFramework.mHostInitialized) {
return newBase;
}
try {
return (Context) ProxyRePluginInternalVar.createActivityContext.call(null, activity, newBase);
} catch (Exception e) {
if (LogDebug.LOG) {
e.printStackTrace();
}
}
return null;
}

这里其实是通过反射调用Factory2的createActivityContext()方法,从RePluginInternal的initLocked()即可看出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void initLocked(final ClassLoader classLoader) {
final String factory2 = "com.qihoo360.i.Factory2";
final String factory = "com.qihoo360.i.Factory";
// 初始化Factory2相关方法
createActivityContext = new MethodInvoker(classLoader, factory2, "createActivityContext", new Class<?>[]{Activity.class, Context.class});
handleActivityCreateBefore = new MethodInvoker(classLoader, factory2, "handleActivityCreateBefore", new Class<?>[]{Activity.class, Bundle.class});
handleActivityCreate = new MethodInvoker(classLoader, factory2, "handleActivityCreate", new Class<?>[]{Activity.class, Bundle.class});
handleActivityDestroy = new MethodInvoker(classLoader, factory2, "handleActivityDestroy", new Class<?>[]{Activity.class});
handleRestoreInstanceState = new MethodInvoker(classLoader, factory2, "handleRestoreInstanceState", new Class<?>[]{Activity.class, Bundle.class});
startActivity = new MethodInvoker(classLoader, factory2, "startActivity", new Class<?>[]{Activity.class, Intent.class});
startActivityForResult = new MethodInvoker(classLoader, factory2, "startActivityForResult", new Class<?>[]{Activity.class, Intent.class, int.class, Bundle.class});
// 初始化Factory相关方法
loadPluginActivity = new MethodInvoker(classLoader, factory, "loadPluginActivity", new Class<?>[]{Intent.class, String.class, String.class, int.class});
}

而Factory2.createActivityContext()方法如下:

1
2
3
4
5
6
7
8
9
10
/**
* @hide 内部方法,插件框架使用
* 插件的Activity创建成功后通过此方法获取其base context
* @param activity
* @param newBase
* @return 为Activity构造一个base Context
*/
public static final Context createActivityContext(Activity activity, Context newBase) {
return sPLProxy.createActivityContext(activity, newBase);
}

sPLProxy是PluginLibraryInternalProxy对象,而PluginLibraryInternalProxy.createActivityContext()方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @hide 内部方法,插件框架使用
* 插件的Activity创建成功后通过此方法获取其base context
* @param activity
* @param newBase
* @return 为Activity构造一个base Context
*/
public Context createActivityContext(Activity activity, Context newBase) {
// 此时插件必须被加载,因此通过class loader一定能找到对应的PLUGIN对象
Plugin plugin = mPluginMgr.lookupPlugin(activity.getClass().getClassLoader());
if (plugin == null) {
...
return null;
}
return plugin.mLoader.createBaseContext(newBase);
}

而Loader.createBaseContext()方法很简单:

1
2
3
final Context createBaseContext(Context newBase) {
return new PluginContext(newBase, android.R.style.Theme, mClassLoader, mPkgResources, mPluginName, this);
}

显然,就是创建一个PluginContext,而这个PluginContext继承自ContextThemeWrapper, 只不过这PluginContext中的ClassLoader是PluginDexClassLoader, Resources对象是对应于插件资源的Resources对象。

所以,PluginActivity中attachBaseContext()中的newBase其实是PluginContext对象。从而在插件中startService()本质上是调用了PluginContext.startService():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public ComponentName startService(Intent service) {
if (mContextInjector != null) {
mContextInjector.startServiceBefore(service);
}
if (mLoader.mPluginObj.mInfo.getFrameworkVersion() <= 2) {
// 仅框架版本为3及以上的才支持
return super.startService(service);
}
try {
return PluginServiceClient.startService(this, service, true);
} catch (PluginClientHelper.ShouldCallSystem e) {
// 若打开插件出错,则直接走系统逻辑
return super.startService(service);
} finally {
if (mContextInjector != null) {
mContextInjector.startServiceAfter(service);
}
}
}

其中ContextInject.startServiceBefore()和startServiceAfter()是起监听作用,这个先不管。那实际上就会走到PluginServiceClient.startService()方法,这个方法的分析在下一节。

2. 启动插件及Service

PluginServiceClient.startService()方法如下:

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
public static ComponentName startService(Context context, Intent intent, boolean throwOnFail) {
// 从 Intent 中获取 ComponentName
ComponentName cn = getServiceComponentFromIntent(context, intent);
// 获取将要使用服务的进程ID,并在稍后获取PSS对象
int process = getProcessByComponentName(cn);
if (process == PROCESS_UNKNOWN) {
// 有可能是不支持的插件,也有可能本意就是想调用Main工程的服务。则直接调用系统方法来做
if (LOG) {
LogDebug.d(PLUGIN_TAG, "PSS.startService(): Call SystemAPI: in=" + intent);
}
if (throwOnFail) {
throw new PluginClientHelper.ShouldCallSystem();
} else {
return context.startService(intent);
}
}
// 既然确认是插件,则将之前获取的插件信息填入
intent.setComponent(cn);
IPluginServiceServer pss = sServerFetcher.fetchByProcess(process);
if (pss == null) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "psc.ss: pss n");
}
return null;
}
// 开启服务
try {
return pss.startService(intent, sClientMessenger);
} catch (Throwable e) {
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "psc.ss: pss e", e);
}
}
return null;
}

这个方法涉及到的东西比较多,主要有获取ComponentName, 进程id, 获取具有IPC能力的IPluginServiceServer代理,最后通过这个代理开启服务。

2.1 构造ComponentName

PluginServiceClient中的getServiceComponentFromIntent()方法如下:

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
/**
* 启动 Service 时,从 Intent 中获取 ComponentName
*/
private static ComponentName getServiceComponentFromIntent(Context context, Intent intent) {
ClassLoader cl = context.getClassLoader();
String plugin = Factory.fetchPluginName(cl);
/* Intent 中已指定 ComponentName */
if (intent.getComponent() != null) {
// 根据 Context 所带的插件信息,来填充 Intent 的 ComponentName 对象。具体见方法说明
return PluginClientHelper.getComponentNameByContext(context, intent.getComponent());
} else {
/* Intent 中已指定 Action,根据 action 解析出 ServiceInfo */
if (!TextUtils.isEmpty(intent.getAction())) {
ComponentList componentList = Factory.queryPluginComponentList(plugin);
if (componentList != null) {
// 返回 ServiceInfo 和 Service 所在的插件
Pair<ServiceInfo, String> pair = componentList.getServiceAndPluginByIntent(context, intent);
if (pair != null) {
return new ComponentName(pair.second, pair.first.name);
}
}
} else {
...
}
}
return null;
}

显然,这里分两种情况,一种是Intent中的ComponentName不为空,那么调用PluginClientHelper.getComponentNameByContext()来获取ComponentName:

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
/**
* 根据Context所带的插件信息,来填充Intent的ComponentName对象
* 若外界将Context.xxxService方法用PluginServiceClient代替(如编译工具所做的),则这里会做如下事情:
* 1、将外界传递的Intent为com.qihoo360.mobilesafe的包名,变成一个特定插件的包名
* 2、这样交给外界的时候,其ComponentName已变成“插件名-类名”的形式,可以做下一步处理
* 3、若其后处理失败,则仍会调用系统的相应方法(但不是该函数的职责)
*
* @param c 根据Context内容来决定如何填充Intent,此为那个Context对象
* @param from ComponentName
* @return 新的ComponentName。或者如果插件有问题,则返回from
*/
public static ComponentName getComponentNameByContext(Context c, ComponentName from) {
if (from == null) {
// 如果Intent里面没有带ComponentName,则我们不支持此特性,直接返回null
// 外界会直接调用其系统的对应方法
return null;
}
String appPackage = IPC.getPackageName();
if (!TextUtils.equals(from.getPackageName(), appPackage)) {
// 自己已填好了要使用的插件名(作为CN的Key),这里不做处理
return from;
}
// 根据Context的ClassLoader来看到底属于哪个插件,还是只是主程序
ClassLoader cl = c.getClassLoader();
String pn = Factory.fetchPluginName(cl);
if (TextUtils.isEmpty(pn)) {
// 获得了无效的插件信息,这种情况很少见,故打出错误信息,什么也不做
if (LOGR) {
LogRelease.e(PLUGIN_TAG, "pch.iibc: pn is n. n=" + from);
}
} else if (TextUtils.equals(pn, RePlugin.PLUGIN_NAME_MAIN)) {
// 此Context属于主工程,则也什么都不做。稍后会直接走“主程序的Context”来做处理
if (LOG) {
LogDebug.d(PLUGIN_TAG, "PluginClientHelper.iibc(): Call Main! n=" + from);
}
} else {
// 将Context所在的插件名写入CN中,待后面的方法去处理
if (LOG) {
LogDebug.d(PLUGIN_TAG, "PluginClientHelper.iibc(): Call Plugin! n=" + from);
}
return new ComponentName(pn, from.getClassName());
}
return from;
}

这个方法其实很简单,就是获取插件名称,然后重新创建一个ComponentName,其中ComponentName的pkg为插件名称,cls为之前的ComponentName中的cls,即真正要启动的Service的名称

再回到PluginServiceClient中的getServiceComponentFromIntent()方法,另外一种情形是根据ComponentList来获取ServiceInfo和Service所在的插件名称,ComponentList.getServiceAndPluginByIntent()方法如下:

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
/**
* 根据 Intent 匹配 Service
* <p>
* 遍历 plugin 插件中,所有保存的 Service 的 IntentFilter 数据,进行匹配,
* 返回第一个符合条件的 ServiceInfo 对象.
*
* @param context Context
* @param intent 调用方传来的 Intent
* @return 匹配到的 ServiceInfo
*/
public Pair<ServiceInfo, String> getServiceAndPluginByIntent(Context context, Intent intent) {
String action = intent.getAction();
if (!TextUtils.isEmpty(action)) {
Set<String> plugins = ManifestParser.INS.getPluginsByActionWhenStartService(action);
if (plugins != null) {
for (String plugin : plugins) {
// 获取 plugin 插件中,所有的 Service 和 IntentFilter 的对应关系
Map<String, List<IntentFilter>> filters = ManifestParser.INS.getServiceFilterMap(plugin);
// 找到 plugin 插件中,IntentFilter 匹配成功的 Service
String service = IntentMatcherHelper.doMatchIntent(context, intent, filters);
ServiceInfo info = Factory.queryServiceInfo(plugin, service);
if (info != null) {
return new Pair<>(info, plugin);
}
}
}
}
return null;
}

这个方法很简单,就是根据Intent中的action进行匹配,匹配成功的话就根据service名称从ComponentList中的mServices成员获取ServiceInfo对象。这个mServices成员是在解析插件时赋值的,包含了插件中所有的Service,关于ComponentList中成员的赋值,在之前的文章中讲过,就不再赘述了。

所以,总结起来,这一小节的内容其实就是由插件中原始的Intent来构造出含有目标Service名称,以及目标Service所在插件的插件名称的ComponentName,根据这些信息,后面就可以启动目标Service.

2.2 获取pid

PluginServiceClient中getProcessByComponentName()方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static int getProcessByComponentName(ComponentName cn) {
if (cn == null) {
// 如果Intent里面没有带ComponentName,则我们不支持此特性,直接返回null
// 外界会直接调用其系统的对应方法
return PROCESS_UNKNOWN;
}
String pn = cn.getPackageName();
// 开始尝试获取插件的ServiceInfo
ComponentList col = Factory.queryPluginComponentList(pn);
if (col == null) {
...
return PROCESS_UNKNOWN;
}
ServiceInfo si = col.getService(cn.getClassName());
if (si == null) {
...
return PROCESS_UNKNOWN;
}
int p = PluginClientHelper.getProcessInt(si.processName);
...
return p;
}

前面分析过,构造出来的ComponentName中的pkgName其实就是插件名称,所以cn.getPackageName()获取的是插件名称,而Factory.queryPluginComponentList()方法如下:

1
2
3
4
5
6
7
8
9
/**
* 警告:低层接口
* 调用此接口会在当前进程加载插件(不加载代码和资源,只获取ComponentList)
* @param name 插件名
* @return 插件的ComponentList
*/
public static final ComponentList queryPluginComponentList(String name) {
return sPluginManager.queryPluginComponentList(name);
}

这里实际上是调用PluginCommImpl的queryPluginComponentList()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 警告:低层接口
* 调用此接口会在当前进程加载插件(不加载代码和资源,只获取ComponentList)
* @param name 插件名
* @return 插件的ComponentList
*/
public ComponentList queryPluginComponentList(String name) {
// 先从缓存获取
ComponentList cl = Plugin.queryCachedComponentList(Plugin.queryCachedFilename(name));
if (cl != null) {
return cl;
}
Plugin p = mPluginMgr.loadPackageInfoPlugin(name, this);
if (p != null) {
return p.mLoader.mComponents;
}
...
return null;
}

这个方法很简单,就是先从缓存中取ComponentName,如果插件已经启动,那么就可以取到,否则,需要调用PmBase的loadPackageInfoPlugin()方法,而调用这个方法会启动插件:

1
2
3
4
final Plugin loadPackageInfoPlugin(String plugin, PluginCommImpl pm) {
Plugin p = Plugin.cloneAndReattach(mContext, mPlugins.get(plugin), mClassLoader, pm);
return loadPlugin(p, Plugin.LOAD_INFO, true);
}

而PmBase.loadPlugin()就涉及插件的安装与加载问题了,关于这个问题,可以看我之前的文章RePlugin解析之插件的安装与加载,这里就不再赘述了。

再回到PluginServiceClient的getProcessByComponentName()方法,获取到ComponentList后,可以从其中获取到ServiceInfo,然后根据ServiceInfo中的processName可以获取到pid,获取方法如下:

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
/**
* 根据进程名称获取进程ID
*
* @param processName 进程名称
*/
public static Integer getProcessInt(String processName) {
if (!TextUtils.isEmpty(processName)) {
// 插件若想将组件定义成在"常驻进程"中运行,则可以在android:process中定义:
// 1. (推荐)":GuardService"。这样无论宿主的常驻进程名是什么,都会定向到"常驻进程"
String pntl = processName.toLowerCase(); //pntl类似"com.qihoo360.replugin.sample.demo1",ppdntl类似":guardservice",对于这种,就把它分配到常驻进程
String ppdntl = HostConfigHelper.PERSISTENT_NAME.toLowerCase();
if (pntl.contains(ppdntl)) {
return IPluginManager.PROCESS_PERSIST;
}
// 2. 和宿主常驻进程名相同,这样也会定向到"常驻进程",但若移植到其它宿主上则会出现问题
String ppntl = IPC.getPersistentProcessName().toLowerCase(); //ppntl是类似"me.ele.repluginhostsample:guardservice"
if (TextUtils.equals(pntl, ppntl)) {
return IPluginManager.PROCESS_PERSIST;
}
// 3. 用户自定义进程时,从 Map 中取数据
// (根据冒号之后的名称来判断是否是自定义进程)
processName = PluginProcessHost.processTail(processName.toLowerCase());
if (PROCESS_INT_MAP.containsKey(processName)) {
return PROCESS_INT_MAP.get(processName);
}
}
return IPluginManager.PROCESS_UI; //对于processName为空或者为插件包名的,直接返回UI进程id
}

这个方法很简单,就分四种情况,前两种就不说了,第三种就是根据进程名和pid的映射来获取pid. 最后一种就是UI进程。

2.3 获取IPluginServiceServer的代理

再回到PluginServiceClient.startService()方法中,下一步就是调用PluginServiceServerFetch的fetchByProcess()方法来获取IPluginServiceServer代理,方法如下:

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
public IPluginServiceServer fetchByProcess(int process) {
if (process == PluginServiceClient.PROCESS_UNKNOWN) {
return null;
}
// 取之前的缓存
IPluginServiceServer pss;
synchronized (PSS_LOCKER) {
pss = mServiceManagerByProcessMap.get(process);
if (pss != null) {
...
return pss;
}
}
// 缓存没有?则去目标进程获取新的
if (LOG) {
LogDebug.d(PLUGIN_TAG, "PluginServiceClient.fsmbp(): Create a new one! p=" + process);
}
try {
if (process == IPluginManager.PROCESS_PERSIST) {
IPluginHost ph = PluginProcessMain.getPluginHost();
pss = ph.fetchServiceServer();
} else {
PluginBinderInfo pbi = new PluginBinderInfo(PluginBinderInfo.NONE_REQUEST);
IPluginClient pc = MP.startPluginProcess(null, process, pbi);
pss = pc.fetchServiceServer();
}
// 挂死亡周期,如果出问题了就置空重来,防止外界调用psm出现DeadObject问题
pss.asBinder().linkToDeath(new PSSDeathMonitor(process, pss.asBinder()), 0);
} catch (Throwable e) {
...
}
if (pss != null) {
synchronized (PSS_LOCKER) {
mServiceManagerByProcessMap.put(process, pss);
}
}
return pss;
}

显然,首先是尝试从mServiceManagerByProcessMap这个缓存中取,如果缓存中取到了就直接返回;否则分两种情况,一种是如果process为IPluginManager.PROCESS_PERSIST,即为常驻进程,那么可以直接从IPluginHost中获取; 如果不是的话,就需要先创建PluginBinderInfo对象,然后调用MP.startPluginProcess()来获取IPluginClient的代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 注:内部接口
*
* @param plugin
* @param process
* @param info
* @return
* @throws RemoteException
* @hide 内部框架使用
*/
public static final IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException {
return PluginProcessMain.getPluginHost().startPluginProcess(plugin, process, info);
}

这里PluignProcessMain.getPluginHost()获取到了IPluginHost的代理,然后通过这个代理调用startPluignProcess()方法,最终通过IPC调用到PmHostSvc中的startPluginProcess()方法:

1
2
3
4
5
6
@Override
public IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException {
synchronized (this) {
return mPluginMgr.startPluginProcessLocked(plugin, process, info);
}
}

这里实际上是调用PmBase的startPluignProcessLocked()方法,这个方法在RePlugin解析-startActivity流程分析一文中分析过,这里不再赘述,简单地说就是通过启动相应进程中的ContentProvider的方法来启动进程并且获取可以控制对应插件进程的IPluginClient代理

获取到IPluginClient之后,通过IPluginClient的fetchServiceServer()来获取IPluginServiceServer代理,这个IPluginServiceServer就是用来管理Service的,它的定义如下:

1
2
3
4
5
6
7
8
9
interface IPluginServiceServer {
ComponentName startService(in Intent intent, in Messenger client);
int stopService(in Intent intent, in Messenger client);
int bindService(in Intent intent, in IServiceConnection conn, int flags, in Messenger client);
boolean unbindService(in IServiceConnection conn);
String dump();
}

再回到PluginServiceClient.startService()方法中,之后就是调用IPluginServiceServer的代理,调用startService()来启动Service.

2.4 IPluginServiceServer.startService()

IPluginServiceServer.startService()会通过IPC最终调用到PluginServiceServer$Stub中的startService()方法:

1
2
3
4
5
6
@Override
public ComponentName startService(Intent intent, Messenger client) throws RemoteException {
synchronized (LOCKER) {
return PluginServiceServer.this.startServiceLocked(intent, client);
}
}

而PluginServiceServer.startServiceLocked()方法如下:

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
// 启动插件Service。说明见PluginServiceClient的定义
ComponentName startServiceLocked(Intent intent, Messenger client) {
intent = cloneIntentLocked(intent);
ComponentName cn = intent.getComponent();
// ProcessRecord callerPr = retrieveProcessRecordLocked(client);
final ServiceRecord sr = retrieveServiceLocked(intent);
if (sr == null) {
return null;
}
if (!installServiceIfNeededLocked(sr)) {
return null;
}
sr.startRequested = true;
// 加入到列表中,统一管理
mServicesByName.put(cn, sr);
...
// 从binder线程post到ui线程,去执行Service的onStartCommand操作
Message message = mHandler.obtainMessage(WHAT_ON_START_COMMAND);
Bundle data = new Bundle();
data.putParcelable("intent", intent);
message.setData(data);
mHandler.sendMessage(message);
return cn;
}

首先获取ComponentName,然后调用retreveServiceLocked()方法获取ServiceRecord,方法如下:

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
// 通过Intent对象创建或获取ServiceRecord服务。涉及到插件信息的获取
private ServiceRecord retrieveServiceLocked(Intent service) {
ComponentName cn = service.getComponent();
ServiceRecord sr = mServicesByName.get(cn);
if (sr != null) {
return sr;
}
Intent.FilterComparison fi = new Intent.FilterComparison(service);
sr = mServicesByIntent.get(fi);
if (sr != null) {
return sr;
}
String pn = cn.getPackageName();
String name = cn.getClassName();
// 看这个Plugin是否可以被打开
if (!RePlugin.isPluginInstalled(pn)) {
...
return null;
}
// 开始尝试获取插件的ServiceInfo
ComponentList col = Factory.queryPluginComponentList(pn);
if (col == null) {
...
return null;
}
ServiceInfo si = col.getService(cn.getClassName());
if (si == null) {
...
return null;
}
// 构建,放入表中
sr = new ServiceRecord(cn, fi, si);
mServicesByName.put(cn, sr);
mServicesByIntent.put(fi, sr);
return sr;
}

显然,首先会从缓存中获取ServiceRecord,如果获取到则直接返回;否则从ComponentList获取ServiceInfo,然后新建ServiceRecord对象,之后将ComponentName, Intent.FilterComparison, ServiceInfo对象都放入ServiceRecord对象中

再回到startServiceLocked()方法中,获取到ServiceRecord对象后,调用installServiceIfNeededLocked()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 判断是否已加载过Service对象,如无则创建它
private boolean installServiceIfNeededLocked(final ServiceRecord sr) {
if (sr.service != null) {
return true;
}
try {
Boolean result = ThreadUtils.syncToMainThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return installServiceLocked(sr);
}
}, 6000);
if (result == null) {
return false;
}
return result;
} catch (Throwable e) {
...
return false;
}
}

这个方法很简单,就是在主线程中调用installServiceLocked(),并且最多等待6000ms, ThreadUtils这个封装做得不错

而installServiceLocked()方法如下:

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
// 加载插件,获取Service对象,并将其缓存起来
private boolean installServiceLocked(ServiceRecord sr) {
// 通过ServiceInfo创建Service对象
Context plgc = Factory.queryPluginContext(sr.plugin); //KP 这里获取的是PluginContext对象
if (plgc == null) {
...
return false;
}
ClassLoader cl = plgc.getClassLoader(); //这里返回的是PluginDexClassLoader
if (cl == null) {
...
return false;
}
// 构建Service对象
Service s;
try {
s = (Service) cl.loadClass(sr.serviceInfo.name).newInstance();
} catch (Throwable e) {
...
return false;
}
// 只复写Context,别的都不做
try {
attachBaseContextLocked(s, plgc);
} catch (Throwable e) {
...
return false;
}
s.onCreate();
sr.service = s;
// 开启“坑位”服务,防止进程被杀
ComponentName pitCN = getPitComponentName();
sr.pitComponentName = pitCN;
startPitService(pitCN);
return true;
}

这个方法其实很简单,就是获取插件的PluginDexClassLoader之后,利用它加载出Service对象,然后调用attachBaseContextLocked()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 通过反射调用Service.attachBaseContext方法(Protected的)
private void attachBaseContextLocked(ContextWrapper cw, Context c) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
if (mAttachBaseContextMethod == null) {
mAttachBaseContextMethod = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
mAttachBaseContextMethod.setAccessible(true);
}
mAttachBaseContextMethod.invoke(cw, c);
// init Application
Field applicationField = Service.class.getDeclaredField("mApplication");
if (applicationField != null) {
applicationField.setAccessible(true);
applicationField.set(cw, c.getApplicationContext());
}
}

注释都说得很清楚了,就是通过反射调用Service的attachBaseContext()方法,并且通过反射,利用PluginContext替换掉Service中mApplication这个成员

再回到installServiceLocked()方法中,在通过反射调用attachBaseContext()方法之后,调用Service.onCreate()方法,然后进行了一个很重要的操作:启动坑位Service.

2.5 启动坑位Service以提高进程优先级

回到installServiceLocked()方法中,首先通过getPitComponentName()来获取坑位Service的ComponentName:

1
2
3
4
5
6
7
// 构建一个占坑服务
private ComponentName getPitComponentName() {
String pname = IPC.getCurrentProcessName();
int process = PluginClientHelper.getProcessInt(pname);
return PluginPitService.makeComponentName(mContext, process);
}

可见,这里是调用根据PluginContext和pid来获取一个对应的坑位Service,而makeComponentName()方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 创建一个可供使用的ComponentName
*
* @param process Process代号(注意,不是ProcessID)
* @return 一个新的CN对象
*/
public static ComponentName makeComponentName(Context c, int process) {
String key = c.getPackageName();
String prefix = PluginPitService.class.getName();
String value;
if (process == IPluginManager.PROCESS_UI) {
value = prefix + "UI";
} else if (process == IPluginManager.PROCESS_PERSIST) {
value = prefix + "Guard";
} else {
value = prefix + "P" + (100 + process); // TODO 因为process可能是负数,所以这里需要+100,将来会优化这里
}
return new ComponentName(key, value);
}

可见,这里分三种情况,如果是UI进程,value为”PluginPitServiceUI”;如果是常驻进程,则为”PluginPitServiceGuard”;如果是其它进程,比如process的值为“-100”,则value为”PluginPitServiceP0”,那么像PluginPitServiceP0这些组件是什么时候生成并打入到Manifest文件中的,答案就在replugin-host-gradle的ComponentsGenerator中,感兴趣的同学可以看一下,其实很简单的,这里就展开来说了。

再回到installServiceLocked()方法中,假设返回的是含有”PluignPitServiceP0”的ComponentName,之后便调用startPitService()启动坑位Service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 开启“坑位”服务,防止进程被杀
private void startPitService(ComponentName pitCN) {
// TODO 其实,有一种更好的办法……敬请期待
...
Intent intent = new Intent();
intent.setComponent(pitCN);
try {
mContext.startService(intent);
} catch (Exception e) {
// 就算AMS出了问题(如system_server挂了,概率极低,和低配ROM有关),最多也就是服务容易被系统回收,但不能让它“不干活”
e.printStackTrace();
}
}

这个方法太简单了,就是普通的startService(),没啥好说了。

2.6 startService()总结

到了这里可见:RePlugin中根本就不通过AMS来启动Service,而是直接利用PluginDexClassLoader就创建了Service对象,然后在主线程中调用Service的attachBaseContext(), onStartCommand()等方法,并且启动与这个Service在同一个进程中的坑位Service以防止进程被杀。

这里虽然也涉及坑位Service,但是却不存在先替换再取回的过程,而是直接启动坑位Service, 这也是startService()比startActivity()简单的原因。而startService()能这么做的根本原因是Service不需要直接与用户交互,没有复杂的生命周期,所以我们直接接管它的回调就能搞定

3.思考题

Q:在插件中能否通过startService()启动其它插件的Service?

A:目前其实RePlugin技术上既不支持,思想上也不赞成这么做,因为它倡导的是各个插件相互独立,从而插件与插件之间是互相不可见的,所以一个插件不会知道另外一个插件中Service的名称或者其action.

下面说说为什么技术上不支持!

其实答案就藏在PluginServiceClient中的getServiceComponentFromIntent()中:

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
private static ComponentName getServiceComponentFromIntent(Context context, Intent intent) {
ClassLoader cl = context.getClassLoader();
String plugin = Factory.fetchPluginName(cl);
/* Intent 中已指定 ComponentName */
if (intent.getComponent() != null) {
// 根据 Context 所带的插件信息,来填充 Intent 的 ComponentName 对象。具体见方法说明
return PluginClientHelper.getComponentNameByContext(context, intent.getComponent());
} else {
/* Intent 中已指定 Action,根据 action 解析出 ServiceInfo */
if (!TextUtils.isEmpty(intent.getAction())) {
ComponentList componentList = Factory.queryPluginComponentList(plugin);
if (componentList != null) {
// 返回 ServiceInfo 和 Service 所在的插件
Pair<ServiceInfo, String> pair = componentList.getServiceAndPluginByIntent(context, intent);
if (pair != null) {
return new ComponentName(pair.second, pair.first.name);
}
}
} else {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "PSS.startService(): No Component and no Action");
}
}
}
return null;
}

其实Factory.fetchPluginName()这个调用,利用当前的PluginContext来获取相应的PluginDexClassLoader对象,再根据这个ClassLoader对象来获得插件名称,也就是说除了从宿主中启动之外,它是默认要启动的Service就在当前发出请求的这个插件中!

而如果要从一个插件启动另一个插件的Service的话,这个假设就不成立了。