IBigerBiger的成长之路

插件化实现(五)BroadcastReceiver插件化

前面两篇文章分别介绍了关于插件Apk中Activity与Service相关的插件化,其中两种不同的插件化方式,其中Service的生命周期没有额外的因素影响,因此我们选择了手动控制其生命周期的方式,对于Activity来说,由于他的生命周期受用户交互影响,所以我们选择还是用系统本身来控制它的生命周期。

相比Activity与Service来说,BroadcastReceiver生命周期要简单很多,对于一个BroadcastReceiver来说,主要的使用是注册BroadcastReceiver, 发送广播信息和接收广播信息,工欲善其事必先利其器,首先我们还是对于BroadcastReceiver相关的源码进行简单的分析。

BroadcastReceiver相关源码分析

前面也有说到使用BroadcastReceiver,主要是关于注册BroadcastReceiver, 发送广播信息和接收广播信息,所以这里相关的分析也是对于这三个部分分别进行。

注册BroadcastReceiver

在Android中关于BroadcastReceiver的注册,主要分为静态和动态两种

  • 静态注册的BroadcastReceiver会从Application启动开始就一直常驻监听,直到Application消亡。

  • 动态注册较之前者会比较灵活,可动态地在需要监听的地方加注册,但要注意在不需要取消注册。

首先分析下关于动态注册,因为动态注册相对来说比较直观,动态注册调用Context的registerReceiver方法,而这个方法的具体实现类则在ContextImpl里面,而这个方法间接调用了registerReceiverInternal方法,源码如下:

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 Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}

又是熟悉的一幕呀,接下来是调用ActivityManagerService的registerReceiver方法,由于这个方法的内容比较多,接下来代码拆分来分析:


1
2
3
4
5
6
7
ProcessRecord callerApp = null;
if (caller != null) {
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
...
}
}

这里获得调用registerReceiver函数的应用程序进程记录块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
List allSticky = null;
// Look for any matching sticky broadcasts...
Iterator actions = filter.actionsIterator();
if (actions != null) {
while (actions.hasNext()) {
String action = (String)actions.next();
allSticky = getStickiesLocked(action, filter, allSticky);
}
} else {
...
}
// The first sticky in the list is returned directly back to
// the client.
Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;

这里先通过getStickiesLocked函数查找一下有没有对应的sticky intent列表存在。什么是Sticky Intent呢?我们在最后一次调用sendStickyBroadcast函数来发送某个Action类型的广播时,系统会把代表这个广播的Intent保存下来,这样,后来调用registerReceiver来注册相同Action类型的广播接收器,就会得到这个最后发出的广播。

这里,假设我们不使用sendStickyBroadcast来发送CounterService.BROADCAST_COUNTER_ACTION类型的广播,于是,这里得到的allSticky和sticky都为null了。

1
2
3
4
5
6
7
8
9
10
11
12
13
ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp,
Binder.getCallingPid(),
Binder.getCallingUid(), receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
...
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
}

这里其实就是把广播接收器receiver保存一个ReceiverList列表中,这个列表的宿主进程是rl.app,这里就是MainActivity所在的进程了,在ActivityManagerService中,用一个进程记录块来表示这个应用程序进程,它里面有一个列表receivers,专门用来保存这个进程注册的广播接收器。接着,又把这个ReceiverList列表以receiver为Key值保存在ActivityManagerService的成员变量mRegisteredReceivers中,这些都是为了方便在收到广播时,快速找到对应的广播接收器的。

1
2
3
4
BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
rl.add(bf);
...
mReceiverResolver.addFilter(bf);

上面只是把广播接收器receiver保存起来了,但是还没有把它和filter关联起来,这里就创建一个BroadcastFilter来把广播接收器列表rl和filter关联起来,然后保存在ActivityManagerService中的成员变量mReceiverResolver中去。

到这里就分析完关于BroadcastReceiver的动态注册流程了,接下来看一下关于BroadcastReceiver的静态注册流程。

关于静态注册,似乎我们无从下手,并没有明确的代码注册过程,但是从上两篇文章关于PackageParser这个类的介绍和使用,我们知道系统会通过PackageParser解析Apk中的AndroidManifest.xml文件,所以系统会在解析AndroidMafest.xml的标签(也即静态注册的广播)的时候保存相应的信息。而Apk的解析过程是在PackageManagerService中进行的,因此静态注册广播的信息存储在PackageManagerService中,这里就不分析流程了,后面也有相关的源码可以论证这个说法。

发送与接收广播信息

由于其实这个两个部分的在源码中是在同一个方法承接起来的,所以一起分析

发送广播信息很简单,就是一句context.sendBroadcast(),而这个sendBroadcast方法的具体实现是在类则在ContextImpl里面,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}

接下来是调用ActivityManagerService的broadcastIntent方法,而broadcastIntent则是调用了broadcastIntentLocked方法,这个方法很长,我们分开分析


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
// 略
} else {
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
}

这里有两个列表receivers和registeredReceivers

  • receivers是对这个广播感兴趣的静态BroadcastReceiver列表;collectReceiverComponents 通过PackageManager获取了与这个广播匹配的静态BroadcastReceiver信息

  • mReceiverResolver存储了动态注册的BroadcastReceiver的信息

1
2
3
4
5
6
7
8
9
10
11
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
...
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}

这里通过broadcastQueueForIntent匹配符合要求的所有BroadcastReceiver,然后通过scheduleBroadcastsLocked通知队列对广播进行处理,这个其实是发送一个Message,通过一系列的调用,最后调用ActivityThread的scheduleRegisteredReceiver方法,如下

1
2
3
4
5
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky) throws RemoteException {
receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
}

这个receiver具体类型是LoadedApk.ReceiverDispatcher.InnerReceiver,即定义在LoadedApk类的内部类ReceiverDispatcher里面的一个内部类InnerReceiver,这里调用它的performReceive函数,如下

1
2
3
4
5
6
7
8
9
10
11
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (!mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
args.sendFinished(mgr);
}
}
}

这个方法创建了一个Args对象,然后把它post到了mActivityThread这个Handler中;我们查看Args类的run方法,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void run() {
...
try {
ClassLoader cl = mReceiver.getClass().getClassLoader(); // Important!! load class
intent.setExtrasClassLoader(cl);
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent); // callback
} catch (Exception e) {
...
}
if (receiver.getPendingResult() != null) {
finish();
}
}

这里已经调用了匹配的BroadcastReceiver的onReceive回调,完成了从发送广播信息与接收广播信息的整个流程。

BroadcastReceiver插件化

关于BroadcastReceiver插件化其实也是有两个点的,一个是动态注册插件中的BroadcastReceiver,另外一个是静态注册插件中的BroadcastReceiver。

关于BroadcastReceiver插件化,通过源码分析以及对于BroadcastReceiver的了解来看,它并没有很强的生命周期,所以其实关于BroadcastReceiver的创建我们更多可以去使用像Service的插件化方式,将创建保留在自己的手中,而不是想Activity一样去设计一套瞒天过海的方式去实现插件化。

静态广播注册

首先说一下关于静态广播的注册,我们知道系统会通过PackageParser解析Apk中的AndroidManifest.xml文件,并将静态的广播信息存储在PackageManagerService中,所以我们这里看起来是无法去实现静态广播的注册,但是实际上静态广播与动态广播并没有什么本质的区别,所以我们可以通过动态广播的方式去将静态广播注册,这样也是有缺陷的,静态BroadcastReceiver与动态BroadcastReceiver一个非常大的不同之处在于:动态BroadcastReceiver在进程死亡之后是无法接收广播的,而静态BroadcastReceiver则可以——系统会唤醒Receiver所在进程,但是这其实影响不大。

对于通过AndroidManifest.xml获取广播信息信息集合与上一篇文章的获取Service类似,这里就直接上代码了,如下

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
Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser");
Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
Object packageParser = packageParserClass.newInstance();
Object packageObj = parsePackageMethod.invoke(packageParser, apkFile, PackageManager.GET_RECEIVERS);
Field receiversField = packageObj.getClass().getDeclaredField("receivers");
//获取receivers集合
List receivers = (List) receiversField.get(packageObj);
Class<?> packageParser$ActivityClass = Class.forName("android.content.pm.PackageParser$Activity");
Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Class<?> userHandler = Class.forName("android.os.UserHandle");
Method getCallingUserIdMethod = userHandler.getDeclaredMethod("getCallingUserId");
int userId = (Integer) getCallingUserIdMethod.invoke(null);
Object defaultUserState = packageUserStateClass.newInstance();
Method generateReceiverInfo = packageParserClass.getDeclaredMethod("generateActivityInfo",
packageParser$ActivityClass, int.class, packageUserStateClass, int.class);
Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component");
Field intentsField = componentClass.getDeclaredField("intents");
for (Object receiver : receivers) {
//获取Receiver对应的 ActivityInfo
ActivityInfo info = (ActivityInfo) generateReceiverInfo.invoke(packageParser, receiver, 0, defaultUserState, userId);
//获取Receiver的IntentFilter
List<? extends IntentFilter> filters = (List<? extends IntentFilter>) intentsField.get(receiver);
sCache.put(info, filters);
ClassLoader cl = null;
if (cl == null) {
File optimizedDirectoryFile = HookApplication.getContext().getDir("dex", Context.MODE_PRIVATE);
cl = new CustomClassLoader(apkFile.getPath(), optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader());
}
for (IntentFilter intentFilter : filters) {
BroadcastReceiver broadcastReceiver = (BroadcastReceiver) cl.loadClass(info.name).newInstance();
//注册BroadcastReceiver
context.registerReceiver(broadcastReceiver, intentFilter);
}
}

那么到这里静态广播的注册就完成了,接下来看一个动态注册

动态注册注册

动态注册的BroadcastReceiver其实可以当作一个普通的Java对象;我们完全可以用纯ClassLoader技术把插件中的Receiver加载进来。

最简单方式如下:

1
2
3
4
5
BroadcastReceiver receiver = null;
File optimizedDirectoryFile = HookApplication.getContext().getDir("dex", Context.MODE_PRIVATE);
ClassLoader cl = new CustomClassLoader(getFileStreamPath("xxxx.apk").getPath(), optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader());
receiver = (BroadcastReceiver) cl.loadClass("xxx.xxx.xxx.BroadcastReceiver").newInstance();
registerReceiver(receiver, new IntentFilter(ACTION));

这里我们直接通过获取插件Apk的ClassLoader插件中的Receiver加载进来并注册。

在我们认为一起都很完美的时候,如果在插件Activity中有动态注册的BroadcastReceiver的代码的话,就会发现报错,错误信息如下

Snip20170325_4.png

这个报错是因为我们插件中动态注册的BroadcastReceiver并非在我们宿主的ProcessRecord进程里面,而且也并非我们上述的动态注册方式,所以我们要想方法去解决这个问题。

我们从调用AcitivityManagerService的registerReceiver方法参数入手,方法如下

1
2
3
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
}

可以看到这5个参数最有问题的是callerPackage与receiver,callerPackage这里应该使用宿主的packageName而并非是插件的packageName,接下来看一下这个receiver

这个receiver的实现是LoadedApk的内部类ReceiverDispatcher的内部类InnerReceiver,IIntentReceiver,这是一个接口,它的实现类是LoadedApk中的ReceiverDispatcher内部类中的InnerReceiver,我们通过反射拿到其中的mDispatcher,这是一个ReceiverDispatcher的WeakReference,然后再通过它拿到我们原始的BroadcastReceiver,它是ReceiverDispatcher中的mReceiver字段。这个IIntentReceiver可以用LoadedApk中的getReceiverDispatcher生成,如下

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
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
rd = map.get(r);
}
}
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}

我们可以通过这个方法来生成一个新的IIntentReceiver来替代原始的IIntentReceiver,

代码如下

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
if ("registerReceiver".equals(method.getName())){
Object rawIIntentReceiver;
int indexBroadcastReceiver = 0;
for (int i = 0; i < args.length; i++) {
if (args[i].getClass().getName().equals("android.app.LoadedApk$ReceiverDispatcher$InnerReceiver")) {
indexBroadcastReceiver = i;
break;
}
}
String packageString;
int indexpackage = 0;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof String) {
indexpackage = i;
break;
}
}
rawIIntentReceiver = args[indexBroadcastReceiver];
Class<?> LoadedApk$ReceiverDispatcher$InnerReceiver = Class.forName("android.app.LoadedApk$ReceiverDispatcher$InnerReceiver");
Field mDispatcherField = LoadedApk$ReceiverDispatcher$InnerReceiver.getDeclaredField("mDispatcher");
mDispatcherField.setAccessible(true);
WeakReference weakReference = (WeakReference)mDispatcherField.get(rawIIntentReceiver);
//获取ReceiverDispatcher对象
Object mDispatcher = weakReference.get();
Class<?> LoadedApk$ReceiverDispatcher = Class.forName("android.app.LoadedApk$ReceiverDispatcher");
Field mReceiverField = LoadedApk$ReceiverDispatcher.getDeclaredField("mReceiver");
mReceiverField.setAccessible(true);
//获取ReceiverDispatcher的mReceiver
BroadcastReceiver mReceiver = (BroadcastReceiver)mReceiverField.get(mDispatcher);
//获取ReceiverDispatcher的mActivityThread
Field mActivityThreadField = LoadedApk$ReceiverDispatcher.getDeclaredField("mActivityThread");
mActivityThreadField.setAccessible(true);
Handler mActivityThread = (Handler)mActivityThreadField.get(mDispatcher);
//获取ReceiverDispatcher的mInstrumentation
Field mInstrumentationField = LoadedApk$ReceiverDispatcher.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(mDispatcher);
//获取ReceiverDispatcher的mRegistered
Field mRegisteredField = LoadedApk$ReceiverDispatcher.getDeclaredField("mRegistered");
mRegisteredField.setAccessible(true);
Boolean mRegistered = (Boolean) mRegisteredField.get(mDispatcher);
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod("getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass);
Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
defaultCompatibilityInfoField.setAccessible(true);
Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null);
ApplicationInfo applicationInfo = generateApplicationInfo(new File(apkFilePath));
//获取插件的loadedApk对象
Object loadedApk = getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);
//获取loadedApk中的getReceiverDispatcher
Method getReceiverDispatcherMethod = loadedApk.getClass().getMethod("getReceiverDispatcher", BroadcastReceiver.class, Context.class, Handler.class,
Instrumentation.class, boolean.class);
getReceiverDispatcherMethod.setAccessible(true);
//生成新的IIntentReceiver
Object rd = getReceiverDispatcherMethod.invoke(loadedApk,mReceiver,HookApplication.getContext(),mActivityThread,mInstrumentation,mRegistered);
//替换IIntentReceiver
args[indexBroadcastReceiver] = rd;
packageString = HookApplication.getContext().getPackageName();
//替换callerPackage
args[indexpackage] = packageString;
return method.invoke(mBase, args);
}
if ("unregisterReceiver".equals(method.getName())){
}

经过这样设置后,就可以正确的启动,至此关于BroadcastReceiver插件化就分析完毕了。

写在后面的话

到这里基本就完成了我们插件这一系统的大部分了,本来下一篇就是关于ContentProvider的插件化相关的知识了,但是现在好像ContentProvider的使用真的是比较少的,在平时开发中,没有特殊的需求,基本这个组件就没有出现的机会,所以这里就不做关于ContentProvider的插件化的讲解了,插件化系列基本到这里就结束了,peace~~~