LocalBroadcastManager 是V4包中的一个类,主要负责程序内部广播的注册与发送。也就是说,它只是适用代码中注册发送广播,对于在AndroidManifest中注册的广播接收,则不适用。

官方英文解释如下:

Helper to register for and send broadcasts of Intents to local objects within your process. This is has a number of advantages over sending global broadcasts with sendBroadcast(Intent):

You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
It is more efficient than sending a global broadcast through the system.

接下来如正题,先看一下全局变量:

private final Context mAppContext;
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers = new HashMap();private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap();private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList();
static final int MSG_EXEC_PENDING_BROADCASTS = 1;
private final Handler mHandler;
private static final Object mLock = new Object();
private static LocalBroadcastManager mInstance;

mAppContext:即ApplicationContext,所以不用担心内存泄漏问题。

mReceivers:记录注册的BroadcastReceiver及其IntentFilter的数组,这里为什么是数组,下面会有讲到。

mActions:记录IntentFilter中的action对应的BroadcastReceiver数组。虽然这里写的是ReceiverRecord类型,但它实际上是一个内部类,主要保存了BroadcastReceiver及其对应的IntentFilter。

mPendingBroadcasts:在发送广播时,会根据Intent的action,找到与之相对应的BroadcastReceiver。还记得吗?action是可以对应多个BroadcastReceiver,所以这里是数组。

mHandler:就做了一件事情,发送广播。

mLock:同步锁。

mInstance:自己本身的实例对象,有经验的可能已经猜到了,没错,LocalBroadcastManager是一个单例。

看一下它的构造方法,标准的单例写法:

public static LocalBroadcastManager getInstance(Context context) {synchronized (mLock) {if (mInstance == null) {mInstance = new LocalBroadcastManager(context.getApplicationContext());}return mInstance;}
}private LocalBroadcastManager(Context context) {this.mAppContext = context;this.mHandler = new Handler(context.getMainLooper()) {public void handleMessage(Message msg) {switch (msg.what) {case MSG_EXEC_PENDING_BROADCASTS:LocalBroadcastManager.this.executePendingBroadcasts();break;default:super.handleMessage(msg);}}};
}

上面谈到的mAppContext是ApplicationContext的证据:mInstance = new LocalBroadcastManager(context.getApplicationContext());

而mHandler就是在构造时创建的,内部就做了一件事,发送广播:executePendingBroadcasts。

接下来就看一下如何注册广播接收者:

public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {synchronized (this.mReceivers) {ReceiverRecord entry = new ReceiverRecord(filter, receiver);ArrayList filters = (ArrayList) this.mReceivers.get(receiver);if (filters == null) {filters = new ArrayList(1);this.mReceivers.put(receiver, filters);}filters.add(filter);for (int i = 0; i < filter.countActions(); i++) {String action = filter.getAction(i);ArrayList entries = (ArrayList) this.mActions.get(action);if (entries == null) {entries = new ArrayList(1);this.mActions.put(action, entries);}entries.add(entry);}}
}

就是将BroadcastReceiver和IntentFilter建立一对多的对应关系。可以通过BroadcastReceiver找到其对应的IntentFilter,也可以通过IntentFilter中的action找到所对应的BroadcastReceiver。这里还要解释一下上面提到的mReceivers中,为什么保存的IntentFilter是数组形式。我们知道,IntentFilter中是可以保存多个action的,这也就是为什么它初始化成1个长度的数组。那么这里的数组的意义,其实很简单,就是能支持传入多个IntentFilter。虽然是支持传入多个IntentFilter,但如果里面的action是同名的话,也还是按同一个处理的,后面代码就能看出,action是以键的方法存起来的。

有注册,就得有取消注册:

public void unregisterReceiver(BroadcastReceiver receiver) {synchronized (this.mReceivers) {ArrayList filters = (ArrayList) this.mReceivers.remove(receiver);if (filters == null) {return;}for (int i = 0; i < filters.size(); i++) {IntentFilter filter = (IntentFilter) filters.get(i);for (int j = 0; j < filter.countActions(); j++) {String action = filter.getAction(j);ArrayList receivers = (ArrayList) this.mActions.get(action);if (receivers != null) {for (int k = 0; k < receivers.size(); k++) {if (((ReceiverRecord) receivers.get(k)).receiver == receiver) {receivers.remove(k);k--;}}if (receivers.size() <= 0)this.mActions.remove(action);}}}}
}

简单来说,就是将BroadcastReceiver从mReceivers和mActions中移除掉。由于BroadcastReceiver是mReceivers的键,所以移除掉比较简单。而mActions就稍微复杂一些,需要根据BroadcastReceiver中的IntentFilter数组,从mActions中移除掉。

还记得注册时,同名的action可以对应不同的BroadcastReceiver吗,注意这里的一句话:if (((ReceiverRecord) receivers.get(k)).receiver == receiver),没错,它只会移除掉当前的,不会将action对应的BroadcastReceiver都删除掉。

最后就是关键的 public boolean sendBroadcast(Intent intent)方法,整个方法都是synchronized (this.mReceivers)的,由于这个方法内容太长,这里分段来讲解:

String action = intent.getAction();
String type = intent.resolveTypeIfNeeded(this.mAppContext.getContentResolver());Uri data = intent.getData();
String scheme = intent.getScheme();
Set categories = intent.getCategories();boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION ) != 0;

首先取出intent中的参数,除了action外,其他都是用来作比较的。如果在intent中setFlag设置了Intent.FLAG_DEBUG_LOG_RESOLUTION,就会可以输出一些log信息。

然后就是从mActions中取出intent的action所对应的ReceiverRecord

ArrayList entries = (ArrayList) this.mActions.get(intent.getAction());ArrayList receivers = null;
for (int i = 0; i < entries.size(); i++)

这段就是for循环里面的内容:

ReceiverRecord receiver = (ReceiverRecord) entries.get(i);if (receiver.broadcasting) {} else {int match = receiver.filter.match(action, type, scheme,data, categories, "LocalBroadcastManager");if (match >= 0) {                        if (receivers == null) {receivers = new ArrayList();}receivers.add(receiver);receiver.broadcasting = true;} else {}}
}

在这里,如果是发送中,就什么也不做。否则,先匹配一下receiver中的IntentFilter,如果匹配上,就设置其为发送中,即设置变量broadcasting为true。其意义在于避免重复发送。

最后,添加到ArrayList中:this.mPendingBroadcasts.add(new BroadcastRecord(intent,receivers));通知Handler,执行executePendingBroadcasts()方法。

if (receivers != null) {for (int i = 0; i < receivers.size(); i++) {((ReceiverRecord) receivers.get(i)).broadcasting = false;}this.mPendingBroadcasts.add(new BroadcastRecord(intent,receivers));if (!this.mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {this.mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);}return true;
}

executePendingBroadcasts()方法就很简单了,就是取出mPendingBroadcasts数组中的BroadcastReceiver(在ReceiverRecord中保存其对象),调用其onReceive方法。

private void executePendingBroadcasts() {while (true) {BroadcastRecord[] brs = null;synchronized (this.mReceivers) {int N = this.mPendingBroadcasts.size();if (N <= 0) {return;}brs = new BroadcastRecord[N];this.mPendingBroadcasts.toArray(brs);this.mPendingBroadcasts.clear();}for (int i = 0; i < brs.length; i++) {BroadcastRecord br = brs[i];for (int j = 0; j < br.receivers.size(); j++)((ReceiverRecord) br.receivers.get(j)).receiver.onReceive(this.mAppContext, br.intent);}}
}

还有一个方法sendBroadcastSync,平常我们一般不会用到这个方法,这里放一下源码:

public void sendBroadcastSync(Intent intent) {if (sendBroadcast(intent))executePendingBroadcasts();
}

这里再补一个其内部类ReceiverRecord的源码:

private static class ReceiverRecord {final IntentFilter filter;final BroadcastReceiver receiver;boolean broadcasting;ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {this.filter = _filter;this.receiver = _receiver;}
}

小结:

1、LocalBroadcastManager在创建单例传参时,不用纠结context是取activity的还是Application的,它自己会取到tApplicationContext。

2、LocalBroadcastManager只适用于代码间的,因为它就是保存接口BroadcastReceiver的对象,然后直接调用其onReceive方法。

3、LocalBroadcastManager注册广播后,当该其Activity或者Fragment不需要监听时,记得要取消注册,注意一点:注册与取消注册在activity或者fragment的生命周期中要保持一致,例如onResume,onPause。

4、LocalBroadcastManager虽然支持对同一个BroadcastReceiver可以注册多个IntentFilter,但还是应该将所需要的action都放进一个IntentFilter,即只注册一个IntentFilter,这只是我个人的建议。

5、LocalBroadcastManager所发送的广播action,只能与注册到LocalBroadcastManager中BroadcastReceiver产生互动。如果你遇到了通过LocalBroadcastManager发送的广播,对面的BroadcastReceiver没响应,很可能就是这个原因造成的。

Android编程之LocalBroadcastManager源码详解相关推荐

  1. Android编程之Intent源码详解

    Intent源码详解,直接开始入题: Intent源码6700多行代码,但真正核心代码 就那么几百行,大部分都用来定义常量字符串了 先来看一下 public class Intent implemen ...

  2. Android编程之DialogFragment源码详解(一)

    DialogFragment是Fragment家族成员之一,如果你把它简单的理解成Dialog,那就错了.它的确可以做作dialog显示,还可以显示出自己定义的Dialog或者AlertDialog, ...

  3. Android编程之ArrayList源码详解及使用

    ArrayList 本是 java中的代码,放到android这边显得不太合适,但由于它在android开发中又大量应用,所以就放到这里,供大家学习. 相信大家对ArrayList不陌生,但要说到细节 ...

  4. Android编程之DialogFragment源码详解(二)

    接上篇,继续: 然后就是setShowsDialog(boolean showsDialog)方法.官方文档是这样解释的:控制DialogFragment是否是一个dislog,如果设置为false, ...

  5. android dismiss方法,Android编程之DialogFragment源码详解(一)注:对 dismissAllowingStateLoss()理解...

    DialogFragment是Fragment家族成员之一,如果你把它简单的理解成Dialog,那就错了.它的确可以做作dialog显示,还可以显示出自己定义的Dialog或者AlertDialog, ...

  6. 基于sqlite的android数据库编程,Android编程之SQLite数据库操作方法详解

    Android编程之SQLite数据库操作方法详解 发布时间:2020-09-07 12:33:04 来源:脚本之家 阅读:85 作者:低调小一 本文实例讲述了Android SQLite数据库操作方 ...

  7. android 远程视频监控程序源码,详解基于Android已开放源代码的远程视频监控系统教程...

    网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.Socket的英文原义是"孔"或"插座".通常也称作"套接字 ...

  8. Android源代码介绍,Android-Log源码详解

    Log.java 源码目录:frameworks/base/core/java/android/util 编译目录:frameworks/base image.png 我惊奇的发现,Log.java里 ...

  9. 源码详解Android 9.0(P) 系统启动流程之SystemServer

    源码详解Android 9.0(P) 系统启动流程目录: 源码详解Android 9.0(P)系统启动流程之init进程(第一阶段) 源码详解Android 9.0(P)系统启动流程之init进程(第 ...

最新文章

  1. linux版视频转换软件,居家旅行必备的Linux视频转换工具
  2. JDK8漫谈——增强接口
  3. OTA和Recovery系统升级流程介绍
  4. OpenGL Lighting models照明模型的实例
  5. python实现knn算法鸢尾花_Python学习之knn实现鸢尾花分类
  6. 编译器和链接器的任务是什么
  7. 工作站的windows server 2008 终于安装好了
  8. 使用ISAPI_Rewrite对asp.net实现URL重写伪静态
  9. 【flink】flink 消费组死掉 Lag不变 kafka不提交 重启恢复 非常诡异
  10. 侧边栏_第四课 侧边栏和过滤器
  11. 搭建一个wordpress
  12. 五分钟学会PowerDesigner创建概念数据模型
  13. 《触动人心—设计优秀的iPhone应用》读书笔记(二)
  14. Firefox中fetch请求后直接调用location.reload(),返回NS_BINDING_ABORTED错误
  15. html微信图片自适应,微信小程序实现图片高度自适应
  16. Oracle 19c rac的搭建
  17. POI使用word模板文件循环输出行并导出word
  18. 为元宇宙提供动力的关键技术驱动力
  19. GameObject在SetActive(false)隐藏后,其下脚本仍会运行
  20. 互联网 Java 工程师1000道面试题(485页)

热门文章

  1. Go语言与数据库开发:01-02
  2. 将DataRow转换为DataTable
  3. 平面设计师必读的十本书
  4. JavaScript 中的函数式编程实践
  5. Under the Hoods of Cache Fusion, GES, GRD and GCS
  6. Node JS Buffer使用理解
  7. 迁移到云端之前需要考虑哪些问题?
  8. 王爽《汇编语言》第2版-----2、寄存器
  9. Exchange2010与Office365混合部署升级到Exchange2016混合部署——Ex2016运行Office365混合配置向导...
  10. lua笔记之userdata