Android 浅析 Broadcast (二) 注册原理

前言

Linus Benedict Torvalds : RTFSC – Read The Funning Source Code

概括

本章通过跟踪registerReceiver(BroadcastReceiver receiver, IntentFilter filter)函数一路深入广播是如何被注册的。

动态注册过程

Step 1.ContextWrapper.registerReceiver()

1
2
3
4
5
6
7
@Override
Context mBase;
...
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}

Step 2.ContextImpl.registerReceiver()

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
final LoadedApk mPackageInfo;
@Override
public Intent registerReceiver(...) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(...) {
return registerReceiverInternal(...);
}
private Intent registerReceiverInternal(...) {
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();
}
}
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
}

通过几层的封装,最终会到ContextImpl.registerReceiverInternal这函数里,mPackageInfo变量是一个LoadedApk对象,LoadedApk是用来负责处理广播的接收。这里面首先会调用mMainThread.getHandler()来获取一个handler,先来看这个函数的过程再往下走。

Step 3.ActivityThread.getHandler()

1
2
3
4
5
6
7
8
9
10
11
final H mH = new H();
...
final Handler getHandler() {return mH;}
...
private class H extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
...
}
}
}

我们看到最终是传回一个handler的对象,有这个对象就可以分发ActivityManagerService发送过来的广播消息给这个进程处理了。

接下来这个registerReceiverInternal函数通过LoadedApk.ReceiverDispatcher()获取一个IIntentReceiver接口对象rd,这是一个Binder对象。

Step 4.LoadedApk.getIIntentReceiver()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final IIntentReceiver.Stub mIIntentReceiver;
...
ReceiverDispatcher(...) {
mIIntentReceiver = new InnerReceiver(this, !registered);
mReceiver = receiver;
mContext = context;
mActivityThread = activityThread;
mInstrumentation = instrumentation;
mRegistered = registered;
mLocation = new IntentReceiverLeaked(null);
mLocation.fillInStackTrace();
}
...
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}

在这个函数里,创建了一个新的Receiver接口,并且将一个Activity和BroadcastReceiver绑定到LoadedApk,这样就可以在LoadedApk里查看是否存在相应的广播接收和发布器了。
在这里创建了一个InnerReceiver对象,这是一个Binder对象,实现了IIntentReceiver接口。最后通过getIIntentReceiver()返回给外部。

Step 5.ActivityManagerNative.registerReceiver()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Intent registerReceiver(...)
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
...
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
...
Intent intent = null;
int haveIntent = reply.readInt();
if (haveIntent != 0) {
intent = Intent.CREATOR.createFromParcel(reply);
}
reply.recycle();
data.recycle();
return intent;
}

这个函数最主要就是通过Binder驱动进入到ActivityManagerService中调用AMS的registerReceiver函数。

Step 6.ActivityManagerService.registerReceiver()

1
2
3
4
5
6
7
8
9
10
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
int callingUid;
int callingPid;
...
}

这个函数比较长,我们分成几个模块来解析。

Part 1

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

这里首先是获得调用registerReceiver函数的应用程序进程记录块,在这个Broadcast的进程记录块里就有MainActivity的启动代码。

Part 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
while (actions.hasNext()) {
String action = actions.next();
..
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
stickyIntents.addAll(intents);
}
}
...
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
...
Intent intent = stickyIntents.get(i);
...
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
Intent sticky = allSticky != null ? allSticky.get(0) : null;

接下来是处理一个粘性的广播,将intents添加到stickyIntents里。代码虽多但功能简单。

Part 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
receiver.asBinder().linkToDeath(rl, 0);
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
mReceiverResolver.addFilter(bf);
...
return sticky;

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

静态注册过程

Step 1.AndroidManifest

1
2
3
4
5
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.unknow.jason.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>

在manifest添加receiver标签并且配上intentfilter。

Step 2.PackageManagerService.scanDirLI()

PMS管理类里面的scanDirLI()函数是安装流程的开吃,无论是开机安装APK、手动还是自动等安装方式,都会走到这个函数里。
scanDirLI()函数主要做的是对于后缀为APK的文件进行解析和安装。这里我们只看解析的函数。
scanDirLI()函数里解析的功能传递给scanPackageLI()函数执行。

Step 3.PackageManagerService.scanPackageLI()

scanPackageLI()函数对APK文件的解析过程是由PackageParser实例执行。该实例执行完解析后会保存在PMS中,其中就包括Receiver信息。
scanPackageLI()函数说调用的PackageParser实例函数是parsePackage()

Step 4.PackageParser.parsePackage()

每一个Apk文件都是一个归档文件,里面包含了Android应用程序的配置文件AndroidManifest.xml,这里主要就是要对这个配置文件进行解析,解析完成后就会从Apk归档文件中得到这个配置文件,然后调用另外一个parsePackage()。
在第二个parsePackage()函数里就会对我们AndroidManifest.xml里的标签进行解析了。
接下来对Receiver的解析存在在”application”里,对”application”的解析是调用parseApplication函数。

Step 5.PackageParser.parseApplication()

1
2
3
4
5
6
7
if (tagName.equals("activity")) {
...
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(...);
...
owner.receivers.add(a);
} ...

这里上一点代码。我们看到这里有两个步骤。
第一:从parseActivity()函数里取得标签的内容。
第二:将它们保存到receivers里面。
我们首先来看parseActivity()函数是如何解析的。

Step 6.PackageParser.parseActivity()

parseActivity()这个函数,“”和“”两个标签的解析都用同一个函数。在解析完这些数据后会返回一个Activity对象,里面就包含了所有标签数据。
当解析完成后会一路沿着传递路线返回,一直返回到Step3。将数据保存在PackageParser.Package对象里。
接下来会调用另一个scanPackageLI()函数。

Step 7.PackageManagerService.scanPackageLI()

这函数其实就是封装了一层,实际的操作函数是scanPackageDirtyLI()。

Step 8.PackageManagerService.scanPackageDirtyLI()

1
2
3
4
5
6
N = pkg.receivers.size();
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
mReceivers.addActivity(a, "receiver");
...
}

然后我们可以看到这里,最后将receiver从package里面获取出来保存到PMS里面的一个全局变量mReceivers里,这样以后想查询就可以直接从PMS这里获取了。

总结

从两个注册的过程来看,我们可以知道receiver无论是从动态注册还是静态注册,最后都是AMS负责将其交互给用户,分别只在于一个字节保存到AMS另一个则要先保存到PMS然后AMS去获取。
过程都不复杂,重点是不要被太多枝叶给分散注意。