Android之BroadcastReceiver

Android 之 BroadcastReceiver

静态注册和动态注册

默认广播sendBroadcast(intent)和有序广播sendOrderedBroadcast(intent)

在注册广播中的 中使用 android:priority 属性(-1000 ~ 1000,数值越大优先级越大);

动态注册广播不是常驻型广播,跟随 Activity 的生命周期。注意在 Activity 结束前,移除广播接收器。静态注册是常驻型,当应用关闭后,如有广播来,程序也会被系统调用自动运行;

当广播为有序广播时,优先级高的先接收(不分动态静态)。同优先级的广播接收器,动态优先于静态。

同优先级的同类广播接收器,静态:先扫描的优于后扫描的;动态:先注册的优于后注册的。

对于动态广播,有注册就必然得有注销,否则会导致内存泄漏

实现原理

Android 中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。将广播的发送者和接收者极大程度解耦,使得系统能够方便集成,更易扩展。

  1. 自定义广播接收者 BroadcastReceiver,并重写 onReceiver()方法;
  2. 通过 Binder 机制向 AMS (Activity Manager Service) 进行注册;
  3. 广播发送者通过 Binder 机制向 AMS 发送广播;
  4. AMS 查找符合相应条件(IntentFilter/Permission 等)的 BroadcastReceiver,将广播发送到 BroadcastReceiver(一般情况下是 Activity)相应的消息循环队列中;
  5. 消息循环执行拿到此广播,回调 BroadcastReceiver 中的onReceive()方法

底层实现原理

静态广播的注册

静态广播是通过 PackageManagerService 在启动的时候扫描已安装的应用去注册的。在其构造方法中,会去扫描应用的安装目录,顺序是先扫描系统应用安装目录在扫描第三方应用。用于扫描的方法PackageManagerService.scanDirLI():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void scanDirLI(File dir, int flags, int scanMode, long currentTime){
String[] files = dir.list();
if(files == null){
return ;
}
int i;
for(i=0; i<files.length; i++){
File file = new File(dir, files[i]);
if(!isPackageFilename(files[i])){
continue;
}
PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
if(pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK){
file.delete();
}
}
}

private static final boolean isPackageFilename(String name){
return name != null && name.endsWith(".apk");
}

scanDirLI()->scanPackageLI()

1
2
3
4
5
6
7
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanMode, long currentTime, UserHandle user){
...
final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags);
...
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);
...
}

scanDirLI()->scanPackageLI()->重载的scanPackageLI()
在这个scanPackageLI()里面会解析 Package 并且将 AndroidManifest.xml 中注册的 BroadcastReceiver 保存下来

1
2
3
4
5
6
7
8
N = pkg.reveivers.size();
r = null;
for(i=0; i<N; i++){
PackageParser.Activity a = pkg.reveivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");// 注册静态广播到了 PackageManagerService 的 mReceivers
...
}

可用PackageManagerService.queryIntentReceivers()查询 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
31
32
33
34
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId){
// ResolveInfo 解析 Intent 过程中返回的信息,实际上就是 AndroidManifest.xml 标签的信息
if(!sUserManager.exists(userId)) return Collections.emptyList();
ComponentName comp = intent.getComponent();
if(comp == null){
if(intent.getSelector() != null){
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if(comp != null){
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if(ai != null){
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}

synchronized (mPackages){
// 这个的作用是?
String pkgName = intent.getPackage();
if(pkgName == null){
return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if(pkg != null){
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers, userId);
}
return null;
}
}

动态广播的注册

我们调用Context.registerReceiver()最后都会调到ActivityManagerService.registerReceiver()

1
2
3
4
5
6
7
8
9
public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId){
...
ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
...
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId);
...
mReceiverResolver.addFilter(bf);
...
}

所以通过mReceiverResolver.queryIntent()就能获得 intent 对应的动态广播了。

发送广播

ContextImpl.sendBroadcast()中会调用ActivityManagerNative.getDefault().broadcastIntent()

1
2
3
4
5
6
7
8
9
public void sendBroadcast(Intent intent){
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolvedTypeIfNeeded(getContentResolver());
try{
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, getUserId());
}catch(RemoteException e){
}
}

实际上调用ActivityManagerService.broadcastIntent():

1
2
3
4
5
6
7
8
9
10
11
12
13
public final int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId){
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this){
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, intent, resolvedType, resultTo, resultCode, resultData, map, requiredPermission, appOp, serialized, sticky, callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}

ActivityManagerService.broadcastIntent()中又会调用ActivityManagerService.broadcastIntentLocked(),其中关键代码:

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
//静态广播
List receivers = null;
//动态广播
List<BroadcastFilter> registeredReceivers = null;
if((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0){
//查询静态广播
receivers = collectReceiverComponents(intent, resolvedType, users);
}
if(intent.getComponent() == null){
//查询动态广播
registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false, userId);
}

final boolean replacePending = (intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
appOp, registeredReceivers, resultTo, resultCode, resultData, map,
ordered, sticky, false, userId);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
// 发送动态广播
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
...
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermission, appOp, receivers, resultTo, resultCode,
resultData, map, ordered, sticky, false, userId);
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
// 发送静态广播
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}

动态广播会优于静态广播,从上面的代码可以看到,这实际是因为 Android 的源代码就是按照这个顺序写的。

ActivityManagerService.collectReceiverComponents(),实际上静态广播就是从 PackageManagerService 中查询的:

1
2
3
4
5
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, int[] users){
...
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
...
}

粘性广播的实现原理(API21已失效)

ActivityManagerService.broadcastIntentLocked()有下面这样一段代码,它将粘性广播存到了 mStickyBroadcasts 中。

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
if(sticky){
...
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if(stickies == null){
stickies = new ArrayMap<String, ArrayList<Intent>>();
mStickyBroadcasts.put(userId, stickies);
}
ArrayList<Intent> list = stickies.get(intent.getAction());
if(list == null){
list = new ArrayList<Intent>();
stickies.put(intent.getAction(), list);
}
int N = list.size();
int i;
for (i=0; i<N; i++) {
if(intent.filterEquals(list.get(i))){
// This sticky already exists, replace it.
list.set(i, new Intent(intent));
break;
}
}
if(i >= N){
list.add(new Intent(intent));
}
}

而 ManagerService.registerReceiver 会获取之前发送的粘性广播,再次发送给刚刚注册的 receiver:

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
List allSticky = null;

// 获取符合的粘性广播
Iterator actions = filter.actionsIterator();
if(actions != null){
while(actions.hasNext()){
String action = (String)actions.next();
allSticky = getStickiesLocked(action, filter, allSticky, UserHandle.USER_ALL);
allSticky = getStickiesLocked(action, filter, allSticky, UserHandle.getUserId
(callingUid));
}
}else{
allSticky = getStickiesLocked(null, filter, allSticky, UserHandle.USER_ALL);
allSticky = getStickiesLocked(null, filter, allSticky, UserHandle.getUserId(callingUid));
}
...
//向新注册的 receiver 发送粘性广播
if(allSticky != null){
ArrayList receivers = new ArrayList();
receivers.add(bf);

int N = allSticky.size();
for (int i=0; i<N; i++) {
Intent intent = (Intent)allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null, null, -1, -1, null, null, AppOpsManager.OP_NONE, reveivers, null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
...

getStickiesLocked()即从 mStickyBroadcasts 中查询之前发送过的粘性广播

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private final List getStickiesLocked(String action, IntentFilter filter, List cur, int userId){
final ContentResolver resolver = mContext.getContentResolver();
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if(stickies == null){
return cur;
}
final ArrayList<Intent> list = stickies.get(action);
if(list == null){
return cur;
}
int N = list.size();
for (int i=0; i<N; i++) {
Intent intent = list.get(i);
if(filter.match(resolver, intent, true, TAG) >= 0){
if(cur == null){
cur = new ArrayList<Intent>();
}
cur.add(intent);
}
}
return cur;
}

广播队列

ActivityManagerService.broadcastIntentLocked()可以看到,实际上它不是直接将广播发送到 BroadcastReceiver 中的,而是将它包装到 BroadcastRecord 中,再放进 BroadcastQueue:

1
2
3
4
5
6
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();

enqueueParallelBroadcastLocked()方法用于并发执行广播的发送:

1
2
3
public void enqueueParallelBroadcastLocked(BroadcastRecord r){
mParallelBroadcasts.add(r);
}

scheduleBroadcastsLocked()就是向 mHandler 发送了个 BROADCAST_INTENT_MSG 消息:

1
2
3
4
5
6
7
public void scheduleBroadcastsLocked(){
if(mBroadcastsScheduled){
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}

mHandler 的工作:

1
2
3
4
5
6
7
8
9
10
final Handler mHandler = new Handler(){
public void handleMessage(Message msg){
case BROADCAST_INTENT_MSG:{
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG:{
broadcastTimeoutLocked(true);
} break;
}
}

processNextBroadcast()用于从队列中获取广播消息并发给 BroadcastReceiver,它内部有两个分支,并行处理和串行处理:

并行处理:例如动态注册的非有序广播等就是使用并行处理:

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
final void processNextBroadcast(boolean fromMsg){
synchronized (mService){
BroadcastRecord r;
mService.updateCpuStats();
if(fromMsg){
mBroadcastsScheduled = false;
}
while(mParallelBroadcasts.size() > 0){
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.currentTimeMills();
final N = r.receivers.size();
for (int i=0; i<N; i++) {
Object target = r.reveivers.get(i);
//发送消息给 Receiver
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
}
addBroadcastToHistoryLocked(r);
}
...
}
...
}

private final void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered){
...
//获取 BroadcastReceiver 的 Binder
r.receiver = filter.receiverList.receiver.asBinder();
...
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.userId);
...
}

void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException{
...
//通过 Binder 将消息处理传到应用进程,应用进程内部再通过 Handler 机制,将消息放到主线程中
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.repProcState);
...
}

串行处理:例如有序广播和静态广播,会通过enqueueOrderedBroadcastLocked()传给 BroadcastQueue:

1
2
3
public void enqueueOrderedBroadcastLocked(BroadcastRecord r){
mOrderedBroadcasts.add(r);
}

然后在processNextBroadcast()里面会对 mOrderedBroadcasts 进行特殊处理…
(我也看不懂啊~)

总结

广播队列传送广播给 Receiver 的原理其实就是将 BroadcastReceiver 和消息都放到 BroadcastRecord 里面,然后通过 Handler 机制遍历 BroadcastQueue 里面的 BroadcastRecord,将消息发送给 BroadcastReceiver。

流程

整个广播机制可以总结成下面这张图:

广播机制流程


参考引用

Android 广播Broadcast的两种注册方式静态和动态
特别感谢 安卓广播的底层实现原理,其实很多都是照着打,还得慢慢看