LocalBroadcastManager 的源码分析及使用方法梳理
Posted 小羊子说
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LocalBroadcastManager 的源码分析及使用方法梳理相关的知识,希望对你有一定的参考价值。
文章目录
- 背景
- LocalBroadcastManager 的优点
- LocalBroadcastManager 源码分析
- LocalBroadcastManager 使用方法:
- 注意事项
- LocalBroadcastManager 弃用原因
- 小结 :
背景
在 android 系统中, BroadcastReceiver 的设计初衷就是从全局考虑的,可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言 BroadcastReceiver 是存在安全性问题的,相应问题及解决如下:
-
当应用程序发送某个广播时系统会将发送的Intent与系统中所有注册的 BroadcastReceiver 的 IntentFilter 进行匹配,若匹配成功则执行相应的 onReceive 函数。可以通过类似 sendBroadcast(Intent, String) 的接口在发送广播时指定接收者必须具备的 permission 。或通过 Intent.setPackage 设置广播仅对某个程序有效。
-
当应用程序注册了某个广播时,即便设置了 IntentFilter 还是会接收到来自其他应用程序的广播进行匹配判断。对于动态注册的广播可以通过类似 registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)的接口指定发送者必须具备的 permission ,对于静态注册的广播可以通过 android:exported=“false” 属性表示接收者对外部应用程序不可用,即不接受来自外部的广播。
最开始在 Android v4 兼容包提供了 android.support.v4.content.LocalBroadcastManager 工具类,
现在需要我们手动引入,添加依赖如下:
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
LocalBroadcastManager 的优点
LocalBroadcastManager 可以帮助大家在自己的进程内进行局部广播发送与注册,使用它比直接通过sendBroadcast(Intent) 发送系统全局广播有以下几点好处:
-
因广播数据在本应用范围内传播,我们不用担心隐私数据泄露的问题;
-
不用担心别的应用伪造广播,造成安全隐患;
-
相比在系统内发送全局广播,它更高效,因为本地广播无需通过 IPC 和其他进程交互。
LocalBroadcastManager 源码分析
源码不多,270 行左右。我们选取一两个角度分析一二。
分析要点: 锁的运用 + 数据结构
单例模式
类的延迟加载的单例模式保证类对象的唯一性。
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context)
synchronized(mLock)
if (mInstance == null)
mInstance = new LocalBroadcastManager(context.getApplicationContext());
return mInstance;
-
这里的 mLock 对象是一个空 Object 对象,只是单纯的在这里用来锁一下。
-
同进程所有线程共享一个 LocalBroadcastManager 实例。而 LocalBroadcastManager 初始化时持有 ApplicationContext,显然其生命周期和整个进程相同。
注意:其他方法基本都涉及到对 mReceivers 的读写改查,因此需要使用同一把锁,形成互斥,但是获取单列和这些方法并不存在数据的交集,因此不需要互斥,所以使用不同的锁。此处和锁定类的效果是一样的。
构造方法
private LocalBroadcastManager(Context context)
mAppContext = context;
// 构造Handler,在主线程回调
mHandler = new Handler(context.getMainLooper())
@Override
public void handleMessage(Message msg)
switch (msg.what)
case MSG_EXEC_PENDING_BROADCASTS:
// 执行队列中的广播
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
;
在 LocalBroadcastManager 构造函数中创建了一个 Handler。
可见 LocalBroadcastManager 的本质上是通过 Handler 机制发送和接收消息的。
在创建 Handler的时候,用了 context.getMainLooper() , 说明这个 Handler 是在 Android 主线程中(UI 线程)创建的,广播接收器的接收消息的时候会在 Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞 UI 从而导致出现 ANR。
重要方法
1. 注册方法:registerReceiver (锁mReceivers)
注册一个匹配指定 IntentFilter 时触发的 BroadcastReceiver。
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter)
// 先获取同步锁
synchronized (mReceivers)
// 用传入的变量构造ReceiverRecord
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
// 从mReceivers查找该receiver是否已经存在记录
ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
// 记录为空创建新的filters
if (filters == null)
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
// key 为 receiver,value 为 ReceiverRecord
filters.add(entry);
// 按照Action记录对应的ReceiverRecord
for (int i=0; i<filter.countActions(); i++)
// 从filter逐个获取Action
String action = filter.getAction(i);
ArrayList<ReceiverRecord> entries = mActions.get(action);
// 这个Action没有记录过则创建新记录
if (entries == null)
entries = new ArrayList<ReceiverRecord>(1);
mActions.put(action, entries);
// key为Action,value为ReceiverRecord
entries.add(entry);
2. 注销广播:unregisterReceiver(锁mReceivers)
public void unregisterReceiver(@NonNull BroadcastReceiver receiver)
synchronized (mReceivers)
final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
// 如果该 Receiver没 有注册过,结束方法的执行
if (filters == null)
return;
for (int i=filters.size()-1; i>=0; i--)
final ReceiverRecord filter = filters.get(i);
filter.dead = true;
for (int j=0; j<filter.filter.countActions(); j++)
final String action = filter.filter.getAction(j);
final ArrayList<ReceiverRecord> receivers = mActions.get(action);
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);
// actions没有接收器,就直接把整个actions记录移除
if (receivers.size() <= 0)
mActions.remove(action);
3. 发送广播:sendBroadcast(锁mReceivers)
public 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();
// 根据发送的action找出已经注册的接收者记录
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
if (entries != null)
ArrayList<ReceiverRecord> receivers = null;
for (int i=0; i<entries.size(); i++)
ReceiverRecord receiver = entries.get(i);
// 同一个ReceiverRecord多次注册相同条件的IntentFilter不会重复通知
if (receiver.broadcasting)
continue;
// 检查此ReceiverRecord是否满足接收事件的条件
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;
if (receivers != null)
for (int i=0; i<receivers.size(); i++)
receivers.get(i).broadcasting = false;
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
// 向消息队列放入消息,表示有广播可以分发
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS))
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
return true;
return false;
通过 Intent 发送广播。先获取线程锁 mReceivers,所以可以多线程操作。广播正在主线程分发的时候也会获取该锁,所以不存在线程安全问题。
通过对这个方法的分析我们可以得知:
LocalBroadcastManager 直接翻译过来为本地广播管理器,本质上就是通过普通的回调实现的。
private void executePendingBroadcasts()
// 这里是死循环,直到广播处理完成后退出
while (true)
final BroadcastRecord[] brs;
// 上线程锁
synchronized (mReceivers)
// 获取待处理广播的数量
final int N = mPendingBroadcasts.size();
if (N <= 0)
return;
// 创建与待处理广播数量相同的数组
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);
// 把广播记录派发给所有事件接收者
if (!rec.dead)
rec.receiver.onReceive(mAppContext, br.intent);
整个派发过程都在主线程上进行,如果接收器处理逻辑耗时,会阻塞主线程。
其他学习的知识点:
- 消息的处理
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS))
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
- HashMap 和 ArrayList 数据的结构的选择与使用
LocalBroadcastManager 使用方法:
和正常注册广播的使用方法类似。
在引入工具类后,创建广播接收器:
private BroadcastReceiver onNotice= new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
// intent can contain anydata
Log.d("test","Broadcast received");
;
注册接收者:
protected void onResume()
super.onResume();
IntentFilter myFilter= new IntentFilter(MyIntentService.ACTION);
LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, myFilter);
取消注册接收者:
protected void onDestroy()
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice);
注意事项
-
LocalBroadcastManager 注册广播只能通过代码注册的方式。传统的广播可以通过代码和 xml 两种方式注册。
-
LocalBroadcastManager注册广播后,一定要记得取消监听,这一步可以解决内存泄漏的问题。
-
LocalBroadcastManager注册的广播,在发送广播的时候务必使用LocalBroadcastManager.sendBroadcast(intent),否则会接收不到广播。
传统的发送广播的方法为:context.sendBroadcast( intent );
LocalBroadcastManager 弃用原因
LocalBroadcastManager
是应用级事件总线,在您的应用中使用了层违规行为;任何组件都可以监听来自其他任何组件的事件。- 它继承了系统
BroadcastManager
不必要的用例限制;开发者必须使用Intent
,即使对象只存在且始终存在于一个进程中。由于同一原因,它未遵循功能级BroadcastManager
。
这些问题同时出现,会对开发者造成困扰。
替换
- 您可以将
LocalBroadcastManager
替换为可观察模式的其他实现。合适的选项可能是LiveData
或响应式流,具体取决于您的用例。
小结 :
LocalBroadcastManager 的使用在 项目中如果已经使用,可以深入了解一下源码,学习一下设计思路并运用到项目中。
LocalBroadcastManager 的使用很简单,不过,随着 LiveData的出现,替换使用已经是必然趋势,后期会再介绍一下 liveData 在项目中的运用。
参考文档:
官方文档:Localbroadcastmanager
通过线程提升性能
以上是关于LocalBroadcastManager 的源码分析及使用方法梳理的主要内容,如果未能解决你的问题,请参考以下文章
LocalBroadcastManager 的源码分析及使用方法梳理
Android 之LocalBroadcastManager原理简析
Android 之LocalBroadcastManager原理简析
学习笔记 Android LocalBroadcastManager的使用