最近在分析一个广播 ANR 问题时,对广播机制的代码流程做了大致的l了解,作为 Android 四大组件之一,广播 在 Android 中的应用非常广泛,这里结合自己阅读源码过程中的笔记从代码流程上的做简要分析,对广播机制中涉及到进程进程优先级的切换等其他部分不做叙述。
本文为广播机制浅析系列第一篇,主要分析发送广播的流程。
广播简述
从处理时机的角度来看,广播被分为有序广播和无序广播。有序广播通过 Context#sendBroadcast()
发送,接收者逐个进行处理,接收顺序可以通过匹配的 intent-filter 的 android:priority
属性来控制,具有相同优先级的接收器将按随机顺序运行,期间可以被中断也可以进行数据的传输,如果处理耗时过长则会发生 ANR,无序广播通过 Context#sendBroadcast()
发送,会同时发送给所有的接收者来处理,不会发生 ANR。
从广播存在时间的角度,可以分为黏性广播和非黏性广播,黏性广播在被所有接收者处理完成后也不会消失,而是会一直保存在系统中,当有新接收者注册后,立即就会收到该广播。
还可以分为前台广播和后台广播,通过给 Intent 添加 flag 可以切换为前台广播:
1
| intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
|
前台广播超时时间为 10s,后台广播为 60s。
注册
应用可以通过静态注册和动态注册来接收广播:
在 AndroidManifest.xml 的 receiver 标签中声明:
1 2 3 4 5 6
| <receiver android:name=".MyBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.INPUT_METHOD_CHANGED" /> </intent-filter> </receiver>
|
MyBroadcastReceiver 需要继承 BroadcastReceiver
,并实现 onReceive(Context, Intent)
方法,PMS 会在应用安装时注册接收器,该接收器会成为应用的一个独立入口点,即如果应用当前未运行,系统会启动应用并发送广播。系统会创建新的 BroadcastReceiver
组件对象来处理它接收到的每个广播。此对象仅在调用 onReceive(Context, Intent)
期间有效。一旦从此方法返回代码,系统便会认为该组件不再活跃。
通过 Context 中提供的对应方法来注册广播接收者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Override public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler) { return registerReceiverInternal(receiver, user.getIdentifier(), filter, broadcastPermission, scheduler, getOuterContext(), 0); }
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; ... if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null, true).getIIntentReceiver(); final Intent intent = ActivityManager.getService().registerReceiverWithFeature( mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd, filter, broadcastPermission, userId, flags); ... }
|
registerReceiverInternal 中参数 broadcastPermission 表示广播所需权限,scheduler 用于指定处理广播的线程,默认情况下为主线程。rd 是 android.app.LoadedApk.ReceiverDispatcher.InnerReceiver 类型:
1 2 3 4
| static final class ReceiverDispatcher { final static class InnerReceiver extends IIntentReceiver.Stub { } }
|
作为 Binder 服务端接收 AMS 在处理广播时的调用。接着则又是是通过 Binder 调用到 ActivityManagerService#registerReceiverWithFeature
中,此时就从注册广播应用进程走到了 SystemServer 进程:
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
| final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>(); public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage, String callerFeatureId, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) { ... synchronized(this) { ... Iterator<String> actions = filter.actionsIterator(); int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) { ... } }
ArrayList<Intent> allSticky = null; if (stickyIntents != null) { final ContentResolver resolver = mContext.getContentResolver(); for (int i = 0, N = stickyIntents.size(); i < N; i++) { Intent intent = stickyIntents.get(i); ... if (filter.match(resolver, intent, true, TAG) >= 0) { ... allSticky.add(intent); } } } ... synchronized (this) { ... ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()); if (rl == null) { rl = new ReceiverList(...); mRegisteredReceivers.put(receiver.asBinder(), rl); } BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId, permission, callingUid, userId, instantApp, visibleToInstantApps); rl.add(bf); if (allSticky != null) { ... final int stickyCount = allSticky.size(); for (int i = 0; i < stickyCount; i++) { Intent intent = allSticky.get(i); BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(...); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } } return sticky; } }
|
之前提到,黏性广播在发送后就会一直存在系统中,从这中可以看出,其是被保存在 ActivityManagerService 的成员变量 mRegisteredReceivers 中,并且当有用户在注册该广播监听时,立马就会收到。如果注册的事非黏性广播,则会将该接收者保存到 mRegisteredReceivers 中。
参考
Android Developer:广播概览