时间:2022-05-30 08:27:46 | 栏目:Android代码 | 点击:次
广播作为四大组件之一,在平时开发过程中会大量使用到,使用方式也是多种多样的,既可以自己在manifest中注册,也可以在java代码中动态注册,既可以接收由系统发出的广播,也可以接受自己定义并发送的广播。广播可以实现进程内以及跨进程之间的通信。从本文开始将分别介绍广播的注册,广播的派发,本地广播(LocalBroadcast)以及Android O上对广播的限制,本文主要介绍广播动态注册。
从注册方式上区分:动态注册以及静态注册(显示广播和隐式广播)
从发送方式上区分:无序广播和有序广播
从处理类型上区分:前台广播和后台广播
从运行方式上区分:普通广播和Sticky广播(已弃用)
从发送者区分:系统广播和自定义广播
此外还有protect broadcast(只允许指定应用可以发送)
sticky广播:系统保存了一部分广播的状态,当你注册的时候,不需要等到下次这个广播发出来,直接把最近上一次发送的这个广播返回给你
以上的这些概念在接下来的介绍中都会逐个涉及。
其中的APP,ContextImpl,LoadedApk,ActivityManagerProxy都在APP本身的进程中,ActivityManagerService在system_server进程中。
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; if (receiver != null) { if (mPackageInfo != null && context != null) { if (scheduler == null) { // 注册receiver的时候可以指定接受recover的Handler // 如果没有指定,则默认用主线程的handler处理 scheduler = mMainThread.getHandler(); } // 获取IIntentReceiver // 这个是一个Binder对象,当广播来临时,用于AMS向客户端发起回调 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 { // binder call至AMS,进行广播注册 final Intent intent = ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags); if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); intent.prepareToEnterProcess(); } return intent; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
参数解析:
receiver:将要注册的receiver
userId:用户空间标志,默认情况下我们都只有一个user,现在一些手机推出的分身,其实就是用的第二个user,这种情况下userid会变,否则默认主空间的都是0
IntentFilter:要注册的广播的filter
broadcastPermission:指定要注册的广播的权限
scheduler:指定广播接受(也就是onReceive)所在的线程,也就是说注册的时候就可以指定好广播处理放在哪个线程,如果receiver中事情太多,可以放在另外一个线程,这样可以避免主线程被卡住
context:通过getOuterContext获取到,前面在介绍context的时候有提到,application/service/activity中获取到的是不一样的
flags:注册广播所携带的flag
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r, Context context, Handler handler, Instrumentation instrumentation, boolean registered) { synchronized (mReceivers) { // 如果Context相同,每个receiver对应一个ReceiverDispatcher 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(); } }
mReceivers是一个二级map,一级key是context,二级key是BroadcastReceiver,value是ReceiverDispatcher。
这里的ReceiverDispatcher又是什么呢?
它是LoadedApk中的一个内部类,保存了这个receiver的信息,用于在广播派发到本进程的时候执行,上面这方法最重要的是getIIntentReceiver,这个就非常重要了,它是一个Binder对象,说在广播注册的时候将这个binder对象传递到了AMS,然后当广播派发到本进程的时候,通过这个binder对象再会调回来,它在ReceiverDispatcher创建的时候创建。
static final class ReceiverDispatcher { // 是一个binder对象 final static class InnerReceiver extends IIntentReceiver.Stub { final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher; final LoadedApk.ReceiverDispatcher mStrongRef; InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) { mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd); mStrongRef = strong ? rd : null; } @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { // 这里就是广播真正派发到本进程的时候从systemserver binder call过来执行的 ... } final IIntentReceiver.Stub mIIntentReceiver; final BroadcastReceiver mReceiver; final Context mContext; final Handler mActivityThread; final Instrumentation mInstrumentation; final boolean mRegistered; final IntentReceiverLeaked mLocation; RuntimeException mUnregisterLocation; boolean mForgotten; ... }
到这里,广播注册在APP进程的流程就走完了,主要做了几件事:
public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) { enforceNotIsolatedCaller("registerReceiver"); ArrayList<Intent> stickyIntents = null; ProcessRecord callerApp = null; final boolean visibleToInstantApps = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0; int callingUid; int callingPid; boolean instantApp; synchronized(this) { if (caller != null) { // 正常来讲caller是发起binder call的客户端进程对应的ApplicationThread对象 // 如果为null则抛异常 callerApp = getRecordForAppLocked(caller); if (callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + Binder.getCallingPid() + ") when registering receiver " + receiver); } if (callerApp.info.uid != SYSTEM_UID && !callerApp.pkgList.containsKey(callerPackage) && !"android".equals(callerPackage)) { throw new SecurityException("Given caller package " + callerPackage + " is not running in process " + callerApp); } callingUid = callerApp.info.uid; callingPid = callerApp.pid; } else { callerPackage = null; callingUid = Binder.getCallingUid(); callingPid = Binder.getCallingPid(); } // 判断caller是否为instant app instantApp = isInstantApp(callerApp, callerPackage, callingUid); userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage); // 获取广播注册的filter中的action封装到list中 Iterator<String> actions = filter.actionsIterator(); if (actions == null) { ArrayList<String> noAction = new ArrayList<String>(1); noAction.add(null); actions = noAction.iterator(); } // mStickyBroadcasts是一个二级map // 一级key是userId,二级key是广播对应的action,value是广播对应intent的list(一般只有一个intent) // 这里是为了查询对于当前user,本次注册的所有action对应的sticky广播的intent int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) { String action = actions.next(); for (int id : userIds) { ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id); if (stickies != null) { ArrayList<Intent> intents = stickies.get(action); if (intents != null) { if (stickyIntents == null) { stickyIntents = new ArrayList<Intent>(); } stickyIntents.addAll(intents); } } } } } ArrayList<Intent> allSticky = null; // 这里不为null表示本次注册的广播中有sticky广播 if (stickyIntents != null) { final ContentResolver resolver = mContext.getContentResolver(); // 查找匹配的sticky广播 for (int i = 0, N = stickyIntents.size(); i < N; i++) { Intent intent = stickyIntents.get(i); // 如果caller是instant app,且intent的flag不允许对instant可见,则跳过 if (instantApp && (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) { continue; } // If intent has scheme "content", it will need to acccess // provider that needs to lock mProviderMap in ActivityThread // and also it may need to wait application response, so we // cannot lock ActivityManagerService here. if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { allSticky = new ArrayList<Intent>(); } allSticky.add(intent); } } } // 直接把最近的一个匹配到的sticky广播返回 Intent sticky = allSticky != null ? allSticky.get(0) : null; // 广播注册的时候receiver是可以为null的,这种情况下这里直接return if (receiver == null) { return sticky; } synchronized (this) { // 校验caller进程是否正常 if (callerApp != null && (callerApp.thread == null || callerApp.thread.asBinder() != caller.asBinder())) { // Original caller already died return null; } // mRegisteredReceivers中存放了所有的已注册的receiver // 每个BroadcastReceiver对应一个InnerReceiver,即Binder对象 // binder对象做key,value是ReceiverList // ReceiverList是一个ArrayList 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 { try { // 如果是新创建的receiver,还需要linkToDeath receiver.asBinder().linkToDeath(rl, 0); } catch (RemoteException e) { return sticky; } rl.linkedToDeath = true; } // 放入mRegisteredReceivers mRegisteredReceivers.put(receiver.asBinder(), rl); } else if (rl.uid != callingUid) { throw new IllegalArgumentException( "Receiver requested to register for uid " + callingUid + " was previously registered for uid " + rl.uid + " callerPackage is " + callerPackage); } else if (rl.pid != callingPid) { throw new IllegalArgumentException( "Receiver requested to register for pid " + callingPid + " was previously registered for pid " + rl.pid + " callerPackage is " + callerPackage); } else if (rl.userId != userId) { throw new IllegalArgumentException( "Receiver requested to register for user " + userId + " was previously registered for user " + rl.userId + " callerPackage is " + callerPackage); } // 每一个IntentFilter对应一个BroadcastFilter BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId, instantApp, visibleToInstantApps); // receiverList中存放了通过这个receiver注册的所有的filter // 每调用一次register就会add一次 rl.add(bf); // mReceiverResolver中存放所有的BroadcastFilter mReceiverResolver.addFilter(bf); // 有匹配的sticky广播,则直接开始调度派发 if (allSticky != null) { ArrayList receivers = new ArrayList(); receivers.add(bf); // 对于每一个sticky广播,创建BroadcastRecord并入队(并行) final int stickyCount = allSticky.size(); for (int i = 0; i < stickyCount; i++) { Intent intent = allSticky.get(i); // 根据flag是否有FLAG_RECEIVER_FOREGROUND判断入队是前台还是后台队列 BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers, null, 0, null, null, false, true, true, -1); // 入队,并行队列 queue.enqueueParallelBroadcastLocked(r); // 启动广播的调度,也就是开始派发广播 queue.scheduleBroadcastsLocked(); } } return sticky; } }
上面主要做了几件事情:
本文从App的一次广播注册发起开始,到systemserver的注册流程,整体上流程还是比较简单的,顾名思义,注册,正是把要注册的广播在systemserver中进行登记,等到这个事件真正来临的时候,从登记中取出需要被通知的receiver,这也就是后面广播的派发了。
从设计模式的角度讲,这正是经典的观察者模式。