目录

  • BroadcastReceiver概述
  • BroadcastReceiver分类
  • registerReceiver流程图
  • 源码解析
  • 总结

1. BroadcastReceiver概述

广播作为四大组件之一,在平时开发过程中会大量使用到,使用方式也是多种多样的,既可以自己在manifest中注册,也可以在java代码中动态注册,既可以接收由系统发出的广播,也可以接受自己定义并发送的广播。广播可以实现进程内以及跨进程之间的通信。从本文开始将分别介绍广播的注册,广播的派发,本地广播(LocalBroadcast)以及Android O上对广播的限制,本文主要介绍广播动态注册。

2. BroadcastReceiver分类

从注册方式上区分:动态注册以及静态注册(显示广播和隐式广播)
从发送方式上区分:无序广播和有序广播
从处理类型上区分:前台广播和后台广播
从运行方式上区分:普通广播和Sticky广播(已弃用)
从发送者区分:系统广播和自定义广播
此外还有protect broadcast(只允许指定应用可以发送)
sticky广播:系统保存了一部分广播的状态,当你注册的时候,不需要等到下次这个广播发出来,直接把最近上一次发送的这个广播返回给你

以上的这些概念在接下来的介绍中都会逐个涉及。

3. registerReceiver流程图


其中的APP,ContextImpl,LoadedApk,ActivityManagerProxy都在APP本身的进程中,ActivityManagerService在system_server进程中。

  1. 首先在APP的进程中发起广播的注册,通过registerReceiver接口,这个方法有很多重载方法,但是最终的入口都是在ContextImpl中,后面会详细介绍
  2. 从之前的Context的学习可以知道,registerReceiver最终调用的实现在ContextImpl
  3. 如果没有指定处理广播的handler,则默认使用主线程的handler
  4. 获取要注册的ReceiverDispatcher,在注册的Context相同的情况下,每个Receiver对应一个ReceiverDispatcher
  5. 通过binder call到systemserver进行广播注册

4. 源码解析

4.1 ContextImpl.registerReceiverInternal

    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

4.2 LoadedApk.getReceiverDispatcher

    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {// 如果Context相同,每个receiver对应一个ReceiverDispatcherLoadedApk.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;}@Overridepublic 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进程的流程就走完了,主要做了几件事:

  1. 获取handler
  2. 获取ReceiverDispatcher
  3. 获取InnerReceiver
  4. 将上面这些连带receiver的相关信息,发起binder call到ActivityManagerService

4.3 ActivityManagerService.registerReceiver

    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 appinstantApp = 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广播的intentint[] 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的,这种情况下这里直接returnif (receiver == null) {return sticky;}synchronized (this) {// 校验caller进程是否正常if (callerApp != null && (callerApp.thread == null|| callerApp.thread.asBinder() != caller.asBinder())) {// Original caller already diedreturn null;}// mRegisteredReceivers中存放了所有的已注册的receiver// 每个BroadcastReceiver对应一个InnerReceiver,即Binder对象// binder对象做key,value是ReceiverList// ReceiverList是一个ArrayListReceiverList 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,还需要linkToDeathreceiver.asBinder().linkToDeath(rl, 0);} catch (RemoteException e) {return sticky;}rl.linkedToDeath = true;}// 放入mRegisteredReceiversmRegisteredReceivers.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对应一个BroadcastFilterBroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId, instantApp, visibleToInstantApps);// receiverList中存放了通过这个receiver注册的所有的filter// 每调用一次register就会add一次rl.add(bf);// mReceiverResolver中存放所有的BroadcastFiltermReceiverResolver.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;}}

上面主要做了几件事情:

  1. 对caller的判断
  2. 遍历action,查询是否有匹配的sticky广播
  3. 将本次注册的广播放到mRegisteredReceivers中记录
  4. 如果是sticky广播,开始派发

5. 总结

本文从App的一次广播注册发起开始,到systemserver的注册流程,整体上流程还是比较简单的,顾名思义,注册,正是把要注册的广播在systemserver中进行登记,等到这个事件真正来临的时候,从登记中取出需要被通知的receiver,这也就是后面广播的派发了。
从设计模式的角度讲,这正是经典的观察者模式。

Android Broadcast原理分析之registerReceiver(一)相关推荐

  1. android 实例源码解释,Android Handler 原理分析及实例代码

    Android Handler 原理分析 Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题 今天就为大家详细剖析下Handler的原理 Handler使 ...

  2. Android LayoutInflater原理分析,带你一步步深入了解View

    Android视图绘制流程完全解析,带你一步步深入了解View(一) 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/12921889 ...

  3. Android JNI原理分析

    引言:分析Android源码6.0的过程,一定离不开Java与C/C++代码直接的来回跳转,那么就很有必要掌握JNI,这是链接Java层和Native层的桥梁,本文涉及相关源码: frameworks ...

  4. android消除锯齿原理分析

    原文地址:https://blog.csdn.net/Apple_hsp/article/details/50833300 前言 在Android中view绘画是很重要的一点,当view重写.surf ...

  5. Android LayoutInflater原理分析,带你一步步深入了解View(一)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/12921889 有段时间没写博客了,感觉都有些生疏了呢.最近繁忙的工作终于告一段落, ...

  6. android 补间动画有停顿,Android动画原理分析(一)----补间动画

    1.基本特点 补间动画(Tween动画),是android最早的动画框架,从Android1.0开始就有. 功能:可以实现移动.旋转.缩放.渐变四种效果以及这四种效果的组合形式. 实现形式:xml和代 ...

  7. Android—Broadcast原理

    registerReceiver registerReceiver方法有很多重载方法,但是最终的入口都是在ContextImpl中, private Intent registerReceiverIn ...

  8. Android LayoutInflater原理分析,带你一步步深入了解View(一) 郭霖学习摘要

    2019独角兽企业重金招聘Python工程师标准>>> public class MainActivity extends Activity {//----------------- ...

  9. 第十八期 Android GPS原理分析《手机就是开发板》

    如果想对Android的整个框架有一个更深层次的感性认识,我们还需要抓住一个点去研究一下.Android按照模块分成很多个系统,比如Audio,Video Out,Camera,Phone,WIFI, ...

最新文章

  1. Java微信对账接口返回值解析
  2. 60个开发者不容错过的免费资源库
  3. MXNet半精度(FP16)
  4. Windows消息备忘录
  5. objective-c ——代码块
  6. Linux 802.11 Driver Developer’s Guide
  7. 2pc_two phase commit详情
  8. 云栖专辑 | 阿里开发者们的第3个感悟:从身边开源开始学习,用过才能更好理解代码
  9. 求给定精度的简单交错序列部分和 (15 分)
  10. linux关机_3.5 开关机命令及7个运行级别《LINUX-centos7-操作系统入门到精通》
  11. My interested stuff(2008-07-18)
  12. php内容模型概念,方便新建各种类型表
  13. ztree 标准得json数据格式_Django+zTree构建组织架构树
  14. 学习编程,最忌纠结!
  15. Cameralink协议
  16. 计算机显卡怎么拨下来,电脑主机显卡怎么拆|电脑显卡的拆卸方法
  17. php amr mp3,php实现微信语音amr文件在线播放方法
  18. html怎么设置div只读,html怎么设置只读状态
  19. SNS2124(OEM博科FC交换机)忘记密码,密码初始化
  20. 让人心动的jQuery插件和HTML5动画

热门文章

  1. 【移动端】如何在移动端调出纯数字键盘
  2. 小程序的 生命周期回调函数onLoad,onReady,onShow
  3. 梁启超家书致思成书-人生之旅途历途甚长,所争决不在一年半月
  4. 【小程序登录的两种方式】
  5. 手机/平板电脑作为pc电脑扩展屏——Splashtop Wired XDisplay使用教程
  6. (附源码)计算机毕业设计SSM大学生心理咨询系统 1
  7. 【计算机网络】——奈氏准则与香农公式,它们之间到底有何区别?
  8. 21年毕业,转行软件测试,薪资10K+,好运气都藏在你的实力里
  9. 多标签分类的Focal loss设计
  10. GPS数据格式的分析