Handler系列文章:

  • Handler源码剖析
  • HandlerThread与IntentService源码剖析
  • LocalBroadcastManager源码剖析

BroadcastReceiver作为android四大组件之一常常被人使用。但无论是普通广播还是有序广播,都是系统全局广播。即发送的广播可以被任何应用程序接收到,但同时我们也能接收来自任何应用程序的广播。

为了解决广播安全性的问题,Google引入了一套本地广播机制。使用本地广播机制发送的广播只能在应用中进行传递,而广播接收器也只能接收来自应用内的广播。

LocalBroadcast简单使用

本地广播的使用和普通广播基本没区别,主要是使用LocalBroadcastManager进行广播发送、注册广播接收器和注销广播接收器。

#daqiActivity.java
//本地广播管理器
private LocalBroadcastManager mBroadcastManager;
//广播接收器
private daqiBroadcastReceiver mBroadcastReceiver;@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);//.....//获取广播管理类实例mBroadcastManager = LocalBroadcastManager.getInstance(daqiActivity.this);//初始化intent拦截器IntentFilter mIntentFilter = new IntentFilter();mIntentFilter.addAction("com.daqi.demo.LOCAL_BROADCAST");//初始化广播接收器mBroadcastReceiver = new daqiBroadcastReceiver();//注册广播接收器mBroadcastManager.registerReceiver(mBroadcastReceiver,mIntentFilter);mButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.daqi.demo.LOCAL_BROADCAST");//发送本地广播mBroadcastManager.sendBroadcast(intent);}});
}@Override
protected void onDestroy() {super.onDestroy();//注销广播接收器mBroadcastManager.unregisterReceiver(mBroadcastReceiver);
}private class daqiBroadcastReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(daqiActivity.this,"本地广播",Toast.LENGTH_SHORT).show();}
}
复制代码

LocalBroadcastManager源码分析

从LocalBroadcastManager.getInstance(this)获取实例可以看出,LocalBroadcastManager是一个单例管理类。

查看LocalBroadcastManager#getInstance()的源码并无异样,继续查看构造方法:

#LocalBroadcastManager.java
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {synchronized (mLock) {if (mInstance == null) {//获取ApplicationContext的contextmInstance = new LocalBroadcastManager(context.getApplicationContext());}return mInstance;}
}private LocalBroadcastManager(Context context) {mAppContext = context;mHandler = new Handler(context.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_EXEC_PENDING_BROADCASTS:executePendingBroadcasts();break;default:super.handleMessage(msg);}}};
}
复制代码

LocalBroadcastManager的构造函数只做了两件事:

1、存储ApplicationContext的Context。

2、初始化一个Handler,并将其绑定在主线程。

LocalBroadcastManager居然初始化了一个Handler,莫非本地广播也是通过Handler实现的?

LocalBroadcastManager内部类

在查看注册广播监听器、注销广播监听器和发送本地广播的源码之前,需要先了解LocalBroadcastManager得两个内部类ReceiverRecord 和 BroadcastRecord。

#LocalBroadcastManager.java
//接收器记录
private static final class ReceiverRecord {final IntentFilter filter;final BroadcastReceiver receiver;//标记是否正在处于广播状态boolean broadcasting;//标记该接收器记录是否有效//false为有效,true为无效。boolean dead;ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {filter = _filter;receiver = _receiver;}//....
}//广播器记录
private static final class BroadcastRecord {//发送广播时的Intent对象final Intent intent;//接收器记录列表final ArrayList<ReceiverRecord> receivers;BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {intent = _intent;receivers = _receivers;}
}
复制代码

从名字上ReceiverRecord-接收器记录,BroadcastRecord-广播器记录,可以很好的理解各类中变量的意义。

ReceiverRecord主要用于存储注册广播接收器时,存储注册广播时传递的参数:IntentFilter 和 BroadcastReceiver。

BroadcastRecord主要存储广播的intent和存储IntentFilter符合该intent.action的ReceiverRecord对象。

LocalBroadcastManager内部集合

#LocalBroadcastManager.java//存储广播接收器和对应接收器记录列表(ArrayList<LocalBroadcastManager.ReceiverRecord>)
//在之前的版本这里value存储的是ArrayList<IntentFilter>
//LocalBroadcastManager.ReceiverRecord中存有IntentFilter,所以存储ArrayList<LocalBroadcastManager.ReceiverRecord>和ArrayList<IntentFilter>道理都一样
private final HashMap<BroadcastReceiver, ArrayList<LocalBroadcastManager.ReceiverRecord>> mReceivers = new HashMap();//String存储action,表示一个action对应多个接收该action的接收器记录(ReceiverRecord)
//(ReceiverRecord内部存储广播接收器和过滤规则)
private final HashMap<String, ArrayList<LocalBroadcastManager.ReceiverRecord>> mActions = new HashMap();//与发送的广播的Action匹配的ReceiverRecord集合
//即存储了广播接收器、广播过滤规则 和 发送广播时的Intent
private final ArrayList<LocalBroadcastManager.BroadcastRecord> mPendingBroadcasts = new ArrayList();
复制代码

LocalBroadcastManager定义了3个集合,简单的说就是:

  • mReceivers    存储广播接收器(BroadcastReceiver)对象 和 其IntentFilter信息。
  • mActions    存储单个action 和 监听该action的接收器记录(ReceiverRecord)列表
  • mPendingBroadcasts    存储发送广播时携带的intent对象 和 符合该intent.action的IntentFilter。

如果看不懂,暂时放下,配合下面的源码分析食用。

分别从广播机制的注册广播监听器、发送广播、注销广播监听器入手查看源码。 先从注册广播监听器入手:

registerReceiver()

#LocalBroadcastManager.javapublic void registerReceiver(@NonNull BroadcastReceiver receiver,@NonNull IntentFilter filter) {//加锁synchronized (mReceivers) {//传递进来的receiver广播接收器与filter广播拦截器会被封装在ReceiverRecord对象中。ReceiverRecord entry = new ReceiverRecord(filter, receiver);//从mReceivers中获取有关该receiver广播接收器的接收器记录(ReceiverRecord)对象ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);//如果没有获取到对应的记录(正常是null)if (filters == null) {//创建一个size为1的ArrayList,并将信息存储进去。filters = new ArrayList<>(1);mReceivers.put(receiver, filters);}//将存储注册广播信息的内容存储到filters中filters.add(entry);//遍历注册时传递进来的IntentFilter的action列表for (int i=0; i<filter.countActions(); i++) {//获取actionString action = filter.getAction(i);//获取特定action对应的接收器记录ArrayList<ReceiverRecord> entries = mActions.get(action);//如果原本没有这个条action的记录if (entries == null) {//则创建一个存进去entries = new ArrayList<ReceiverRecord>(1);mActions.put(action, entries);}//将这次注册广播监听器生成的接收器记录对象存储到该action对应的接收器记录(ReceiverRecord)器列表中。entries.add(entry);}}
}
复制代码

总的来说,LocalBroadcastManager#registerReceiver()做了三件事:

  • 将注册广播监听器的信息:IntentFilter 和 BroadcastReceiver。封装在ReceiverRecord中。
  • 为广播接收器添加IntentFilter,存储在mReceivers中。
  • 为注册时IntentFilter中的action添加接收器记录(ReceiverRecord),存储在mActions中。

unregisterReceiver()

再查看注销广播接收器的源码:

#LocalBroadcastManager.javapublic void unregisterReceiver(@NonNull BroadcastReceiver receiver) {//加锁synchronized (mReceivers) {//移除该广播接收器的IntentFilter规则final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);if (filters == null) {return;}//遍历其存储的所有IntentFilterfor (int i=filters.size()-1; i>=0; i--) {//取出其中一个ReceiverRecord对象,其实是获取其对应的IntentFilterfinal ReceiverRecord filter = filters.get(i);//将该接收器记录标志为失效(死亡)filter.dead = true;//遍历这个IntentFilter过滤规则的actionfor (int j=0; j<filter.filter.countActions(); j++) {//获取其中一个actionfinal String action = filter.filter.getAction(j);//依据该action获取对应的ReceiverRecord列表,即获取其对应的广播接收器(BroadcastReceiver)列表final ArrayList<ReceiverRecord> receivers = mActions.get(action);//如果该action的接收器记录(ReceiverRecord)列表不为空if (receivers != null) {for (int k=receivers.size()-1; k>=0; k--) {//倒序或许更快final ReceiverRecord rec = receivers.get(k);if (rec.receiver == receiver) {//找到则将其移除。rec.dead = true;receivers.remove(k);}}//如果接收器记录(ReceiverRecord)列表移除后为空,即该action无对应的广播接收器,则将该action移除。if (receivers.size() <= 0) {mActions.remove(action);}}}}}
}
复制代码

注销广播接收器和注册广播接收器做了相反的事情:

  • 在mReceivers中,移除广播接收器和其IntentFilter。
  • 在mActions中,移除IntentFilter#action对应的接收器记录(ReceiverRecord),若action无对应的接收器记录(ReceiverRecord),则将action移除。

sendBroadcast()

#LocalBroadcastManager.javapublic boolean sendBroadcast(@NonNull Intent intent) {synchronized (mReceivers) {//获取一堆intent的参数final String action = intent.getAction();final String type = intent.resolveTypeIfNeeded(mAppContext.getContentResolver());final Uri data = intent.getData();final String scheme = intent.getScheme();final Set<String> categories = intent.getCategories();//根据intent对象的action获取其对应的接收器记录(ReceiverRecord)列表ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());//如果该action的接收器记录(ReceiverRecord)列表不为空if (entries != null) {//..//这个列表用来记录实际用来接收该广播的广播接收器(BroadcastReceiver)ArrayList<ReceiverRecord> receivers = null;for (int i=0; i<entries.size(); i++) {//获取单个接收器记录,即获取单个广播接收器ReceiverRecord receiver = entries.get(i);//..//如果当前广播接收器正在广播,则先跳过,防止重复注册if (receiver.broadcasting) {//..continue;}//测试此过滤器是否与给定的意图数据匹配。 只有当Intent中的操作和类别与过滤器匹配时,匹配才会成功int match = receiver.filter.match(action, type, scheme, data,categories, "LocalBroadcastManager");//匹配成功if (match >= 0) {//..//如果存储实际用来接收该广播的广播接收器列表没初始化,则初始化。if (receivers == null) {receivers = new ArrayList<ReceiverRecord>();}//将该广播接收器存储到列表中,并设置为正在广播的状态。receivers.add(receiver);receiver.broadcasting = true;} else {//..}}//最后接受广播的广播接收器列表不为空,则将intent对象传递进去。if (receivers != null) {for (int i=0; i<receivers.size(); i++) {//恢复不是正在广播的状态receivers.get(i).broadcasting = false;}//将该广播的intent对象和广播接收器列表封装成接收器记录,存储在mPendingBroadcasts中。mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {//发送特定what的Message,交由Handler处理。 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);}return true;}}}return false;
}
复制代码

sendBroadcast()做了4个步骤:

1、获取广播的intent信息,用于后续的匹对。

2、通过广播的action在mActions中获取其对应的接收器记录(ReceiverRecord)列表。

3、遍历接收器记录(ReceiverRecord)列表,将列表内元素的IntentFilter对象与广播的intent对象进行匹对,如果匹对成功,则存储在新的接收器记录(ReceiverRecord)列表receivers中。

4、将新的接收器记录(ReceiverRecord)列表receivers 和 广播的intent对象存储在广播记录(BroadcastRecord)对象中,并将该广播记录(BroadcastRecord)对象存储在mPendingBroadcasts中。

5、发送what = MSG_EXEC_PENDING_BROADCASTS的Message,让Handler传递广播intent对象到对应的接收器中。

注:对列表receivers存储接收器记录过程中,将接收器记录中的broadcasting标记为true,可以防止重复注册广播接收器造成的多次广播。

重归Handler的handleMessage方法中:

#LocalBroadcastManager.java
@Override
public void handleMessage(Message msg) {switch (msg.what) {case MSG_EXEC_PENDING_BROADCASTS:executePendingBroadcasts();break;default:super.handleMessage(msg);}
}
复制代码

what = MSG_EXEC_PENDING_BROADCASTS的Message,会调用executePendingBroadcasts进行处理。

#LocalBroadcastManager.java
void executePendingBroadcasts() {while (true) {final BroadcastRecord[] brs;synchronized (mReceivers) {//获取待广播的广播记录对象。final int N = mPendingBroadcasts.size();if (N <= 0) {return;}mPendingBroadcasts//将广播记录BroadcastRecord对象存储在数组中,并清空mPendingBroadcasts对象。brs = new BroadcastRecord[N];mPendingBroadcasts.toArray(brs);mPendingBroadcasts.clear();}//解锁//遍历广播记录数组for (int i=0; i<brs.length; i++) {final BroadcastRecord br = brs[i];//遍历接收器记录列表,即广播接收器列表final int nbr = br.receivers.size();for (int j=0; j<nbr; j++) {final ReceiverRecord rec = br.receivers.get(j);//广播接收器列表没有被标记失效,则进行onReceive()回调进行广播。if (!rec.dead) {rec.receiver.onReceive(mAppContext, br.intent);}}}}
}
复制代码

executePendingBroadcasts()将mPendingBroadcasts转换为数组,遍历广播记录数组。对还生效的接收器对象进行onReceive()回调,进行广播。

注:当广播接收器移除时,ReceiverRecord#dead会被标记为true,则标记为无效。

再回看3个列表对象:

  • mReceivers    存储广播接收器(BroadcastReceiver)对象 和 其IntentFilter信息。
    作用:注销广播接收器时,通过广播接收器对象,寻找到其IntentFilter列表信息,方便根据IntentFilter列表中的action信息在mActions中进行移除。
  • mActions    存储单个action 和 监听该action的接收器记录(ReceiverRecord)列表。
    作用:发送广播时,根据广播中intent对象的action,可以快速寻找到其对应接收的接收记录对象,再根据接收器对象的IntentFilter与广播中intent对象做匹配,寻找符合该广播intent对象条件的广播接收器对象。
  • mPendingBroadcasts    存储发送广播时携带的intent对象 和 符合该intent.action的IntentFilter。
    作用:短暂存储需要传递的广播intent对象和接收的该intent对象广播接收器。

总结:

本地广播其实也是使用handler机制,将广播接收器与action、IntentFilter相关联。因为Handler的原因,本地广播也就只能在该应用内接收。

转载于:https://juejin.im/post/5cd4ee27f265da035f6ff2f2

LocalBroadcastManager源码剖析相关推荐

  1. 老李推荐:第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发 1...

    老李推荐:第14章4节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-端口转发 在初始化HierarchyViewer的实例过程中, ...

  2. JS魔法堂:mmDeferred源码剖析

    一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...

  3. Kafka源码剖析 —— 网络I/O篇 —— 浅析KafkaSelector

    为什么80%的码农都做不了架构师?>>>    ##NioSelector和KafkaSelector有什么区别? 先说结论,KafkaSelector(org.apache.kaf ...

  4. Mongoose源码剖析:Introduction and Installation

    引言 要剖析Mongoose的源码,首先你得知道它的一些基本情况和特性.并去使用它.本文就是介绍Mongoose是个什么东西?及如何安装和使用?这里假设你知道什么web服务器软件.web服务器使用什么 ...

  5. 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用 1...

    老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用 上一节我们描述了monkey的命令处理入口函数run是如何调用optionPro ...

  6. 老李推荐:第3章3节《MonkeyRunner源码剖析》脚本编写示例: MonkeyImage API使用示例 1...

    老李推荐:第3章3节<MonkeyRunner源码剖析>脚本编写示例: MonkeyImage API使用示例 在上一节的第一个"增加日记"的示例中,我们并没有看到日记 ...

  7. Mongoose源码剖析:外篇之web服务器

    引言 在深入Mongoose源码剖析之前,我们应该清楚web服务器是什么?它提供什么服务?怎样提供服务?使用什么协议?客户端如何唯一标识web服务器的资源?下面我们抛开Mongoose,来介绍一个we ...

  8. STL源码剖析学习七:stack和queue

    STL源码剖析学习七:stack和queue stack是一种先进后出的数据结构,只有一个出口. 允许新增.删除.获取最顶端的元素,没有任何办法可以存取其他元素,不允许有遍历行为. 缺省情况下用deq ...

  9. 一个完整的python项目源码-一个Python开源项目-腾讯哈勃沙箱源码剖析(上)

    前言 2019年来了,2020年还会远吗? 请把下一年的年终奖发一下,谢谢... 回顾逝去的2018年,最大的改变是从一名学生变成了一位工作者,不敢说自己多么的职业化,但是正在努力往那个方向走. 以前 ...

最新文章

  1. 美国0封伊朗已经6天了,伊石油出口真归零了吗?
  2. 重新定义旅游网站,米胖新版发布
  3. 视图插入数据_用EXCEL作数据分析--招聘信息
  4. 科学家风采|郑纬民:任中国计算机学会理事长四年的三点回忆
  5. 计算机彻底删除删除后如何恢复,如何完全恢复从计算机删除的文件?
  6. excel的常用公式
  7. 解决 Exchange 2013提示“出现意外错误,无法处理您的请求”,无法打开OWA和ECP...
  8. BDA,CDA,CPDA哪个证相对可靠?哪个含金量高?具体考试内容?
  9. IPCAS1.2.11安装步骤
  10. Gilbert Elliot丢包模型
  11. linux vi面板如何复制一行
  12. 北极熊秀舞步神似美国明星
  13. css文件插入背景音乐,博客园添加背景音乐,背景效果!
  14. BI领导驾驶舱-企业管理者不可或缺的决策帮手
  15. Flutter 3.3 正式发布
  16. 解决NETAPP硬盘故障
  17. Mirai QQ机器人(开源)
  18. 1400软件 rslogix_Rslogix 500软件各版本之间的差异、差别或区别
  19. 《Journal of Renewable and Sustainable Energy》期刊介绍(SCI 4区)
  20. 51单片机之温湿度检测系统(DTH11、OLED、LCD1602)

热门文章

  1. CSS特性 之渐变 (全网最全)
  2. 网站服务器不要开启BBR,CentOS 7 网络开启BBR优化(升级内核、开启 BBR)
  3. c语言教学系统设计报告,C语言在线教学系统的设计与实现
  4. ciftiTools | 用于读取、写入、可视化和操作CIFTI文件的R包
  5. java 给qq邮箱发邮件_java实现163邮箱发送邮件到qq邮箱成功案例
  6. 洛谷P1640 连续攻击游戏 二分图匹配
  7. hibernate mysql 例子_Hibernate的基本功能:对数据库的增删改查(创建对象实例)...
  8. 开发人工智能项目编程语言的选择
  9. 自相关-能量密度谱,互相关-互能量密度谱,系统识别
  10. 益和VA 5 正式版 全新功能深度体验