前面分析了Android系统的广播机制,从本质来说,它是一种消息订阅/发布机制。因此,使用这种消息驱动模型的第一步便是订阅消息;而对Android应用程序来说,订阅消息其实就是注册广播接收器。

接下来,我们继续分析Android应用程序是如何注册广播接收器的,以及把广播接收器注册到哪里去的。

在Android的广播机制中,ActivityManagerService扮演着广播中心的角色,负责系统中所有广播的注册和分发操作。因此,Android应用程序注册广播接收器的过程就是把广播接收器注册到AMS的过程。Android应用程序是通过调用ContextWrapper类的registerReceiver方法来把广播接收器BroadcastReceiver注册到AMS中去的,而ContextWrapper类本身又借助ContextImpl类来注册广播接收器。

在Android应用程序框架中,Activity和Service类都继承了ContextWrapper类(ContextWrapper类是Context类的包装类,ContextImpl类是Context类的实现类,调用ContextWrapper类的方法也就会调用到ContextImpl类中的方法),因此,我们可以在Activity或Service的子类中调动registerReceiver方法来注册广播接收器。

下面我们进入主题:

静态广播直接在AndroidManifest.xml中注册一下即可。

动态广播的注册在代码中需要调用Context类的registerReceiver方法,最终调用到ContextImpl类中的registerReceiver方法中。

ContextImpl.registerReceiverInternal

我们看下方法调用:

    @Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {return registerReceiver(receiver, filter, null, null);}@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler) {return registerReceiverInternal(receiver, getUserId(),filter, broadcastPermission, scheduler, getOuterContext());//broadcastPermission为null,scheduler为null}
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {IIntentReceiver rd = null;if (receiver != null) {if (mPackageInfo != null && context != null) {//mPackageInfo是LoadedApk对象,在创建ContextImpl对象的时候指定了LoadedApk对象if (scheduler == null) {//没有设置Handler时,使用主线程的Handlerscheduler = mMainThread.getHandler();//这个handler用来分发AMS发送过来的广播用的}rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);//获取IIntentReceiver对象,传递到AMS,用于接收广播(广播分发器)} else {if (scheduler == null) {scheduler = mMainThread.getHandler();}rd = new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null, true).getIIntentReceiver();}}try {final Intent intent = ActivityManagerNative.getDefault().registerReceiver(mMainThread.getApplicationThread(), mBasePackageName,rd, filter, broadcastPermission, userId);//这里调用到AMS中的registerReceiver方法if (intent != null) {intent.setExtrasClassLoader(getClassLoader());intent.prepareToEnterProcess();}return intent;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

通过两个函数的中转,最终调用到ContextImpl.registerReceiverInternal()。我们先看一下这几个参数,receiver表示用户注册广播接收器;userId注册广播应用程序的userID;filter为广播接收器的接收条件;broadcastPermission广播接收器的权限信息,广播发送者必须带上这个权限信息,它所发出的广播才能被注册者接收到;scheduler表示接收广播的线程的Handler。

方法中使用到了成员变量mPackageInfo是一个LoadedApk实例,它是用来负责处理广播的接收的,在后面一篇讲到广播的发送时(sendBroadcast),会详细描述。参数broadcastPermission和scheduler都为null,而参数context是上面的方法通过调用getOuterContext()得到的,这里它就是指向注册广播的Activity了,因为Activity是继承于Context类的,因此,这里用Context类型来引用。

由于条件mPackageInfo != null和context != null都成立,而且条件scheduler == null也成立,于是就调用mMainThread.getHandler来获得一个Handler了,这个Hanlder是后面用来分发ActivityManagerService发送过的广播用的。这里的成员变量mMainThread是一个ActivityThread实例。我们先来看看ActivityThread.getHandler函数的实现,然后再回过头来继续分析ContextImpl.registerReceiverInternal函数。

ActivityThread.getHandler()

public final class ActivityThread {......final H mH = new H();private final class H extends Handler {......public void handleMessage(Message msg) {......switch (msg.what) {......}......}......}......final Handler getHandler() {return mH;}......}

有了这个Handler之后,就可以分发消息给应用程序处理了。

再回到上一步的ContextImpl.registerReceiverInternal函数中,它通过mPackageInfo.getReceiverDispatcher函数获得一个IIntentReceiver接口对象rd,这是一个Binder对象,接下来会把它传给ActivityManagerService,ActivityManagerService在收到相应的广播时,就是通过这个Binder对象来通知MainActivity来接收的。

我们也是先来看一下mPackageInfo.getReceiverDispatcher函数的实现,然后再回过头来继续分析ContextImpl.registerReceiverInternal函数。

LoadedApk.getReceiverDispatcher

    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {LoadedApk.ReceiverDispatcher rd = null;ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;if (registered) {//上面传递的参数为truemap = mReceivers.get(context);//根据key值获取对应的value,key为context对象if (map != null) {rd = map.get(r);//根据key值,获取对应的value;key为BroadcastReceiver,value为ReceiverDispatcher}}if (rd == null) {rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);//创建LoadedApk.ReceiverDispatcherif (registered) {if (map == null) {map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();mReceivers.put(context, map);}map.put(r, rd);//将创建的LoadedApk.ReceiverDispatcher保存到map数组中}} else {rd.validate(context, handler);}rd.mForgotten = false;return rd.getIIntentReceiver();//获取IIntentReceiver.stub对象(InnerReceiver)}}
    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

在LoadedApk.getReceiverDispatcher函数中,首先看一下参数r是不是已经有相应的ReceiverDispatcher存在了,如果有,就直接返回了;否则就新建一个ReceiverDispatcher,并且以r为Key值保在一个HashMap中,而这个HashMap以Context(注册广播接收器的Activity)为Key值保存在LoadedApk的成员变量mReceivers中。这样,只要给定一个Activity和BroadcastReceiver,就可以查看LoadedApk里面是否已经存在相应的广播接收分发器ReceiverDispatcher了。

final class LoadedApk {......static final class ReceiverDispatcher {final static class InnerReceiver extends IIntentReceiver.Stub {final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;......InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);......}......}......final IIntentReceiver.Stub mIIntentReceiver;//接收AMS发送的广播final Handler mActivityThread;//用于分发广播......ReceiverDispatcher(BroadcastReceiver receiver, Context context,Handler activityThread, Instrumentation instrumentation,boolean registered) {......mIIntentReceiver = new InnerReceiver(this, !registered);//创建InnerReceiver对象mActivityThread = activityThread;       ......}......IIntentReceiver getIIntentReceiver() {//getIIntentReceiver方法获取IIntentReceiver.Stub对象return mIIntentReceiver;}}......
}

在新建广播接收分发器ReceiverDispatcher时,会在构造函数里面创建一个InnerReceiver实例,这是一个Binder对象,实现了IIntentReceiver接口,可以通过ReceiverDispatcher.getIIntentReceiver函数来获得,获得后就会把它传给ActivityManagerService,以便接收广播。在ReceiverDispatcher类的构造函数中,还会把传进来的Handle类型的参数activityThread保存下来,以便后面在分发广播的时候使用。       现在,再回到ContextImpl.registerReceiverInternal函数,在获得了IIntentReceiver类型的Binder对象后,就开始要把它注册到ActivityManagerService中去了。

ActivityManagerService.registerReceiver()

这个方法逻辑比较清楚,但是涉及到很多变量,我们先梳理一下这几个变量:

mStickyBroadcasts:保存了当前系统中所有用户的Sticky广播,key为广播的action,value为该action对应的所有广播Intents;

    /*** State of all active sticky broadcasts per user.  Keys are the action of the每个用户的所有sticky广播* sticky Intent, values are an ArrayList of all broadcasted intents with内层key值是sticky广播的action,value是action对应的所有Intents* that action (which should usually be one).  The SparseArray is keyed* by the user ID the sticky is for, and can include UserHandle.USER_ALL* for stickies that are sent to all users.最外层的key值是userID(包括UserHandle.USER_ALL)*/final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

mRegisteredReceivers:保存了已经注册的所有广播;

mReceiverResolver:保存当前系统注册的所有的BroadcastFilter;

    /*** Resolver for broadcast intents to registered receivers.已注册广播的Intents解析器* Holds BroadcastFilter (subclass of IntentFilter).保存BroadcastFilter*/final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver= new IntentResolver<BroadcastFilter, BroadcastFilter>()

BroadcastRecord:需要发送的一条广播记录,里面的receivers成员存储了需要接收当前广播的所有接收器。

下面我们分块来分析registerReceiver方法。

过滤出与当前注册的IntentFilter中的action匹配的所有的Intent

第一步就是先找出与注册action相匹配的所有的Intent。

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {enforceNotIsolatedCaller("registerReceiver");ArrayList<Intent> stickyIntents = null;ProcessRecord callerApp = null;int callingUid;int callingPid;synchronized(this) {if (caller != null) {callerApp = getRecordForAppLocked(caller);//获取ProcessRecord对象......callingUid = callerApp.info.uid;callingPid = callerApp.pid;} else {callerPackage = null;callingUid = Binder.getCallingUid();callingPid = Binder.getCallingPid();}userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,ALLOW_FULL_ONLY, "registerReceiver", callerPackage);//获取用户IDIterator<String> actions = filter.actionsIterator();//获取当前IntentFilter中所有的actionsif (actions == null) {ArrayList<String> noAction = new ArrayList<String>(1);noAction.add(null);actions = noAction.iterator();}// Collect stickies of users收集与注册用户(userId)相关的所有已经被广播过的Intents,存储在stickyIntents中int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };//包括所有用户,以及注册进程所对应的用户while (actions.hasNext()) {//第一层:遍历actionsString action = actions.next();for (int id : userIds) {//第二层:遍历与调用进程相关的用户idArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);//用户id发送过的所有的sticky广播if (stickies != null) {ArrayList<Intent> intents = stickies.get(action);//获取action对应的所有广播Intentsif (intents != null) {//如果已经发送的Intents里面包含上面的action,则保存在stickyIntents中if (stickyIntents == null) {stickyIntents = new ArrayList<Intent>();}stickyIntents.addAll(intents);}}}}}

从mStickyBroadcasts里面找出发送给所有用户和调用者userID的所有的Sticky广播的Intents,然后和当前注册的IntentFilter中所有的action逐个比较,找出符合的所有的intent,存储在stickyIntents里面。注意,这里仅仅通过action进行了一次筛选。

从stickyIntents里面找出与当前注册的IntentFilter匹配的Intent

上面第一步找出了与注册的action相匹配的Intent,然后在这些Intent中再进行精确匹配,找出精确匹配的Intent。

上面根据action对相应用户的stickyIntents进行了一次过滤,下面根据IntentFilter.match方法对筛选出来的stickyIntents进行精确的匹配,包括action、type、scheme、data、categories等,将最终的匹配结果存放在allSticky里面。如果调用registerReceiver方法传递的receiver是null,就返回allSticky中的第一个匹配的Intent,或者返回null。

        ArrayList<Intent> allSticky = null;if (stickyIntents != null) {final ContentResolver resolver = mContext.getContentResolver();// Look for any matching sticky broadcasts...for (int i = 0, N = stickyIntents.size(); i < N; i++) {//再把stickyIntents里面的intent逐个比较Intent intent = stickyIntents.get(i);// 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) {//match方法中会对action、type、scheme、data等进行比较if (allSticky == null) {allSticky = new ArrayList<Intent>();}allSticky.add(intent);}}}// The first sticky in the list is returned directly back to the client.查找的是stickyIntent,Intent sticky = allSticky != null ? allSticky.get(0) : null;//如果存在满足action条件的已经广播的intent,立即发送一次广播if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);if (receiver == null) {return sticky;}

上面的两部分主要从sticky广播的历史记录中筛选出与当前注册的IntentFilter匹配的历史Intent,将最终的结果保存在allSticky中。如果注册的广播是非sticky广播,一般来说allSticky为null,但是对于网络切换这种发送的时候是以sticky广播形式发送的,这里allSticky就不是null。

在注册记录中查找当前的receiver

        synchronized (this) {if (callerApp != null && (callerApp.thread == null|| callerApp.thread.asBinder() != caller.asBinder())) {// Original caller already diedreturn null;}ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());//以receiver为key值,获取对应的IntentFilter列表,初始为nullif (rl == null) {rl = new ReceiverList(this, callerApp, callingPid, callingUid,userId, receiver);//创建ReceiverList对象if (rl.app != null) {//ProcessRecordrl.app.receivers.add(rl);} else {try {receiver.asBinder().linkToDeath(rl, 0);} catch (RemoteException e) {return sticky;}rl.linkedToDeath = true;}mRegisteredReceivers.put(receiver.asBinder(), rl);//保存receiver对应的ReceiverList} else if (rl.uid != callingUid) {throw new IllegalArgumentException("Receiver requested to register for uid " + callingUid+ " was previously registered for uid " + rl.uid);} else if (rl.pid != callingPid) {throw new IllegalArgumentException("Receiver requested to register for pid " + callingPid+ " was previously registered for pid " + rl.pid);} else if (rl.userId != userId) {throw new IllegalArgumentException("Receiver requested to register for user " + userId+ " was previously registered for user " + rl.userId);}

首先判断当前进程是否还活着;然后从当前系统所有已经动态注册的mRegisteredReceivers中查找当前注册的receiver,返回对应的ReceiverList,该类继承了ArrayList<BroadcastFilter>,语义就是一个receiver可以对应着一串BroadcastFilter,所以说对于同一个receiver对象而言,可以多次调用registerReceiver方法注册不同的广播条件BroadcastFilter,这里面是注册动作中最关键的地方,将receiver以及对应的ReceiverList存储到AMS.mRegisteredReceivers中。

将当前注册的广播过滤器放到ReceiverList里面

            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId);//根据filter创建BroadcastFilter(IntentFilter)广播过滤器rl.add(bf);//将BroadcastFilter加入ReceiverList中if (!bf.debugCheck()) {Slog.w(TAG, "==> For Dynamic broadcast");}mReceiverResolver.addFilter(bf);//将bf添加到mReceiverResolver中,当AMS接收到广播时就可以从mReceiverResolver中找到接收者

根据当前的IntentFilter创建BroadcastFilter对象,BroadcastFilter继承于IntentFilter,基本上和IntentFilter没有太大的区别,里面还包含对ReceiverList的引用。

创建完BroadcastFilter后,将其加到mReceiverResolver中,内部用的是一个ArraySet,说明重复添加完全相同的BroadcastFilter对象不会多次添加。到这里没有匹配的sticky广播已经注册结束了。

我们这里再看一下mReceiverResolver这个变量,mReceiverResolver变量用来存储系统应用进程中所有receiver动态注册的所有的BroadcastFilter,也就是说动态注册的BroadcastReceiver最终保存到了这里。

发送匹配过滤器的sticky广播

            // Enqueue broadcasts for all existing stickies that match// this filter.如果存在满足条件的sticky intents,需要立即发送这些广播if (allSticky != null) {ArrayList receivers = new ArrayList();receivers.add(bf);final int stickyCount = allSticky.size();for (int i = 0; i < stickyCount; i++) {//遍历allSticky,逐个发送IntentIntent intent = allSticky.get(i);BroadcastQueue queue = broadcastQueueForIntent(intent);//获取BroadcastQueueBroadcastRecord r = new BroadcastRecord(queue, intent, null,null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,null, 0, null, null, false, true, true, -1);//创建BroadcastRecordqueue.enqueueParallelBroadcastLocked(r);//将BroadcastRecord对象条件到BroadcastQueue.mParallerlBraodcasts数组中queue.scheduleBroadcastsLocked();//尝试推动一次发送广播的行为,但是如果目前有广播还在发送的处理过程中,本次尝试失败}}return sticky;

上面注册结束以后,如果筛选出与当前注册的IntentFilter匹配的sticky广播的Intent,就将所有匹配的Intent逐条发送广播给当前的注册者receiver,可以看到这里的接收者receivers里面就只有当前创建的一个BroadcastFilter,也就是当前的注册者。

上面就是动态广播注册的整个过程,主要就是将当前注册的动态广播接收器以及对应的广播过滤器BroadcastFilter添加到AMS.mRegisteredReceivers和AMS.mReceiverResolver中,同时处理了与sticky广播的相关逻辑。从这里可以看到对于注册sticky广播而言,在注册结束以后,系统会立马发送与之匹配的sticky广播。

下面继续分析广播发送流程:Android广播管理三--广播发送(sendBroadcast)流程分析

Android广播管理二--广播注册(registerReceiver)流程分析相关推荐

  1. 高通Android智能平台环境搭建_编译流程分析

    高通Android智能平台环境搭建_编译流程分析 高通平台环境搭建,编译,系统引导流程分析 TOC \o \h \z \u 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境 ...

  2. 分布式系统认证解决方案SpringSecurityOAuth2.0(二)分布式系统认证流程分析与实现

    目录 文章 一.简介 1.1 OAuth2.0协议角色 1.2 SpringSecurityOAuth2.0 二.认证流程 三.授权服务器--代码实现 3.1 主要依赖 3.2 Authorizati ...

  3. Android 安装apk流程,Android PMS(二)-Apk安装流程

    原创内容,转载请注明出处,多谢配合. 一.APK组成 在APK的安装流程,在此之前先简单了解下APK组成: 目录/文件 描述 assert 存放的原生资源文件,通过AssetManager类访问. l ...

  4. soul网关系列(六):客户端注册soul流程分析

    探寻spring mvc客户端初始化接入soul至入库的流程 目录 一.soul的数据流综述 二.客户端的流程 2.1 接入soul网关的流程回顾 2.2 soul-spring-boot-start ...

  5. Android 4.0按键事件以及系统流程分析

    Android 4.0中按键的处理流程 按键在Android系统中,有着不同的代表意义.以前的全键盘的手机代码没有阅读过,所以也不是很了解.本人介绍的是在触摸屏的手机上的按键消息的处理流程. 在现在触 ...

  6. Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析

    uboot启动流程分析如下: 第一阶段: a -- 设置cpu工作模式为SVC模式 b -- 关闭中断,mmu,cache v -- 关看门狗 d -- 初始化内存,串口 e -- 设置栈 f -- ...

  7. 高通android智能平台环境搭建_编译流程分析,高通平台环境搭建,编译,系统引导流程分析参考...

    高通有两个cpu,他们分别跑不同的系统,应用程序(ap)端是android系统,modem 端是高通自己的系统. 要编译出可供烧写使用的镜像文件需要三部分代码: 1) 获取经过高通打补丁的 andro ...

  8. Android 内存管理 amp;Memory Leak amp; OOM 分析

    1.Android 流程管理&内存 Android主要应用在嵌入式设备其中.而嵌入式设备因为一些众所周知的条件限制,通常都不会有非常高的配置,特别是内存是比較有限的. 假设我们编写的代 码其中 ...

  9. Alian解读SpringBoot 2.6.0 源码(二):启动流程分析之监听器解析

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.记录应用启动的开始时间 三.初始化启动上下文 3.1.初始化启动上下文 3.2.初始化应用程序事件广播器 3.3.初始化应用上下文 ...

最新文章

  1. windows获取文件夹下所有文件名的方法
  2. 11、MySQL常见错误代码一览表
  3. 笔记 - AliCloud CDN 分发网络简介
  4. Nginx 代理服务的配置说明
  5. C#获2021年度编程语言奖,开发者们最该关注这些亮点!
  6. 解决 java.net.ConnectException: Connection refused: connect 异常
  7. 在网页中通过百度地图查看某地的经纬度
  8. 音乐标签编辑器 Meta 2.0.0 for Mac
  9. 英伟达Nvidia旧版本驱动下载
  10. LayoutInflater原理分析
  11. 3D打印技术到底有多强大?
  12. debian 7 安装 rz sz lrzsz
  13. 学习Flash制作高射炮游戏
  14. 饿了么“短平快”创新项目的架构取舍之道
  15. 虚拟机可以ping通主机,主机不能ping通虚拟机的解决办法
  16. Android电子书阅读器开发笔记(1):创建电子书阅读器项目
  17. PhotoShop如何去除图片上的污点、水印
  18. SecureCRT 终端仿真程序 v7.0.0.326 中文绿色便携破解版 地址
  19. C语言递归:斐波那契数列
  20. windows下app爬虫环境搭建:python + fiddler + Appium + 夜神模拟器

热门文章

  1. 技术人员谈管理之项目群的核心特征
  2. 一句话解释清楚什么是正向传播和反向传播
  3. uniapp微信小程序登陆-升级版,登陆加登录信息过期以后的无痕登陆
  4. 可视化大屏就是“面子工程”?那是你压根不了解大屏的真正功能
  5. 新手学Python之学习官网教程(十三: What Now)
  6. 【初学音频】Android的Audio系统之AudioTrack
  7. ant design pro Routes和authority用法及搭配使用1
  8. 提升方法(boost)
  9. 测试开发实战|如何利用 xUnit 框架对测试用例进行维护?
  10. 广播信道及CSMA/CD协议