最近在分析一个广播 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:广播概览