LocalBroadcastManager原理分析及应用
Posted aspook
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LocalBroadcastManager原理分析及应用相关的知识,希望对你有一定的参考价值。
引言
android页面或模块之间通信的方法有很多,如Intent传值、startActivityForResult、EventBus(RxBus)等,大家追求的无非是解耦以及高灵活性;我们自己的应用中使用了基于Android消息机制封装的一套通信体系,且不谈这些,今天的主角是本地广播。
本地广播是系统提供的一种应用内通信方式,它跟全局广播相比,其运行机制是不同的。全局广播是系统级别的,需要跨进程调用,而且作用域是整个设备,而本地广播的作用域只在当前应用之内,因此无需IPC的过程,本应用发送的本地广播,其他应用接收不到,其他应用发送的本地广播,本应用也接收不到。
本地广播的优点
- 本地广播作用于App内部,广播数据不会泄露,本应用也不会接收到其他应用的广播,因此安全性较高
- 本地广播是在应用内部执行,无需跨进程逻辑,与普通广播相比效率更高
- 使用简单,无需静态注册
原理分析
本地广播的原理也很简单,大概流程如下
- 首先需要接收广播的页面创建一个BroadcastReceiver(广播声明跟全局广播是一样的)并注册到一个应用内的本地广播接收器列表(注册方式跟全局广播不同,下文会详细说明)
- 某个页面利用Intent发送广播(发送方式跟全局广播不同,下文会详细说明)
- 本地广播接收器列表根据IntentFilter匹配得到可以接收该广播的BroadcastReceiver,然后匹配的广播接收器会响应广播(注意这里区分广播的异步与同步发送)
- 在适当时候反注册BroadcastReceiver,比如Activity的onDestroy()回调中
为了方便大家的使用,Android提供了LocalBroadcastManager类(姑且称之为本地广播管理器),该类帮我们封装了注册、反注册、广播发送等过程,接下来分析一下源码实现。
LocalBroadcastManager定义
首先LocalBroadcastManager定义为一个单例,方便App内全局使用。
private static LocalBroadcastManager mInstance;
public static LocalBroadcastManager getInstance(Context context)
synchronized (mLock)
if (mInstance == null)
mInstance = new LocalBroadcastManager(context.getApplicationContext());
return mInstance;
private LocalBroadcastManager(Context context)
mAppContext = context;
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实例,并且是运行在主线程的,它响应一种what为MSG_EXEC_PENDING_BROADCASTS
的消息,这是用来处理异步广播的,具体如何处理稍后说明。
两个内部类
对于广播接收器的注册过程,并非简单地把BroadcastReceiver进行注册,而是进行了简单的包装,这里涉及到LocalBroadcastManager的两个内部类,如下:
ReceiverRecord
private static class ReceiverRecord
final IntentFilter filter;
final BroadcastReceiver receiver;
boolean broadcasting;
ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver)
filter = _filter;
receiver = _receiver;
@Override
public String toString()
StringBuilder builder = new StringBuilder(128);
builder.append("Receiver");
builder.append(receiver);
builder.append(" filter=");
builder.append(filter);
builder.append("");
return builder.toString();
顾名思义,ReceiverRecord简单封装了BroadcastReceiver和IntentFilter。
BroadcastRecord
private static class BroadcastRecord
final Intent intent;
final ArrayList<ReceiverRecord> receivers;
BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers)
intent = _intent;
receivers = _receivers;
BroadcastRecord也很简单,封装了广播Intent以及能够匹配该Intent的接收器列表(这里并非直接是BroadcastReceiver,而是包装类ReceiverRecord)。
基于上述两个包装类,LocalBroadcastManager提供了如下两个HashMap用来存储注册的接收器
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
= new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions
= new HashMap<String, ArrayList<ReceiverRecord>>();
此外还有一个叫mPendingBroadcasts的容器如下
private final ArrayList<BroadcastRecord> mPendingBroadcasts
= new ArrayList<BroadcastRecord>();
它是用来存储待处理广播的列表,其元素为BroadcastRecord。
注册过程
注册过程源码如下,具体流程分析请看注释
/**
* Register a receive for any local broadcasts that match the given IntentFilter.
*
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
*
* @see #unregisterReceiver
*/
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
// 加锁同步
synchronized (mReceivers)
// 将BroadcastReceiver和IntentFilter封装成ReceiverRecord
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
// BroadcastReceiver作为HashMap的key,注意一般来说注册时一个BroadcastReceiver对应一个IntentFilter。但有一种情况下同一个页面内使用同一个BroadcastReceiver,配合不同的IntentFilter注册多次;更极端情况是应用内只有一个BroadcastReceiver,使用不同的IntentFilter进行多次注册
ArrayList<IntentFilter> filters = mReceivers.get(receiver);
if (filters == null)
filters = new ArrayList<IntentFilter>(1);
mReceivers.put(receiver, filters);
filters.add(filter);
// 一个IntentFilter可以对应多个action
for (int i=0; i<filter.countActions(); i++)
String action = filter.getAction(i);
ArrayList<ReceiverRecord> entries = mActions.get(action);
if (entries == null)
entries = new ArrayList<ReceiverRecord>(1);
mActions.put(action, entries);
entries.add(entry);
反注册过程
对应的反注册过程如下,代码逻辑请看注释
/**
* Unregister a previously registered BroadcastReceiver. <em>All</em>
* filters that have been registered for this BroadcastReceiver will be
* removed.
*
* @param receiver The BroadcastReceiver to unregister.
*
* @see #registerReceiver
*/
public void unregisterReceiver(BroadcastReceiver receiver)
// 加锁同步
synchronized (mReceivers)
// 同一个BroadcastReceiver可能对应多个IntentFilter,移除以BroadcastReceiver为key的键值对
ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
if (filters == null)
return;
for (int i=0; i<filters.size(); i++)
IntentFilter filter = filters.get(i);
// 每个IntentFilter可对应多个action
for (int j=0; j<filter.countActions(); j++)
String action = filter.getAction(j);
// 以action作为key
ArrayList<ReceiverRecord> receivers = mActions.get(action);
if (receivers != null)
for (int k=0; k<receivers.size(); k++)
// 移除包含目标BroadcastReceiver的ReceiverRecord
if (receivers.get(k).receiver == receiver)
receivers.remove(k);
k--;
if (receivers.size() <= 0)
mActions.remove(action);
异步发送广播
异步发送广播利用了Android消息机制,发送完成后即可返回,广播会异步响应,通常情况下我们都是使用异步方式,否则会被阻塞。
/**
* Broadcast the given intent to all interested BroadcastReceivers. This
* call is asynchronous; it returns immediately, and you will continue
* executing while the receivers are run.
*
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
*
* @see #registerReceiver
*/
public boolean sendBroadcast(Intent intent)
synchronized (mReceivers)
// 提取出Intent中的action、type、data、scheme、categories等,稍后用于匹配
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();
// debug仅用于打印log,可忽略
final boolean debug = DEBUG ||
((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
if (debug) Log.v(
TAG, "Resolving type " + type + " scheme " + scheme
+ " of intent " + intent);
// 首先根据广播Intent的action进行匹配,匹配不上则直接返回false
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
if (entries != null)
if (debug) Log.v(TAG, "Action list: " + entries);
// receivers用来存储匹配上的接收器包装类ReceiverRecord
ArrayList<ReceiverRecord> receivers = null;
for (int i=0; i<entries.size(); i++)
ReceiverRecord receiver = entries.get(i);
if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
if (receiver.broadcasting)
if (debug)
Log.v(TAG, " Filter's target already added");
continue;
// InterFilter的匹配,若匹配成功,返回值match>=0
int match = receiver.filter.match(action, type, scheme, data,
categories, "LocalBroadcastManager");
if (match >= 0)
if (debug) Log.v(TAG, " Filter matched! match=0x" +
Integer.toHexString(match));
if (receivers == null)
receivers = new ArrayList<ReceiverRecord>();
// 匹配成功的接收器加入列表,并设置一个标志
receivers.add(receiver);
receiver.broadcasting = true;
else
// 匹配失败在debug为true的情况下仅输出log
if (debug)
String reason;
switch (match)
case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
default: reason = "unknown reason"; break;
Log.v(TAG, " Filter did not match: " + reason);
// 匹配成功的接收器
if (receivers != null)
// 将上述匹配成功时设置的标志重新置为false
for (int i=0; i<receivers.size(); i++)
receivers.get(i).broadcasting = false;
// 将匹配成功的接收器封装为BroadcastRecord,加入待处理广播列表
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
// 利用Handler消息机制,发送消息,异步处理广播
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS))
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
return true;
return false;
可以看到异步广播最终利用Android消息机制切换到主线程执行广播接收过程,具体实现逻辑则是在LocalBroadcastManager的构造器中:
private LocalBroadcastManager(Context context)
mAppContext = context;
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);
;
最终调用方法为executePendingBroadcasts()
,稍后详解。
同步发送广播
同步发送广播比较简单,实现如下
/**
* Like @link #sendBroadcast(Intent), but if there are any receivers for
* the Intent this function will block and immediately dispatch them before
* returning.
*/
public void sendBroadcastSync(Intent intent)
if (sendBroadcast(intent))
executePendingBroadcasts();
具体的广播执行也是在executePendingBroadcasts()
中。
执行广播
private void executePendingBroadcasts()
while (true)
BroadcastRecord[] brs = null;
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++)
BroadcastRecord br = brs[i];
for (int j=0; j<br.receivers.size(); j++)
br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
上述代码逻辑比较简单,从mPendingBroadcasts中遍历执行每个BroadcastReceiver的onReceive方法,触发BroadcastReceiver声明页面的回调。在回调中,我们就可以实现自己的广播接收逻辑了。
本地广播的响应逻辑为
@Override
public void onReceive(Context context, Intent intent)
// 处理逻辑
因此如果要传值的话则要依赖于Intent的能力。
注意事项
- 发送广播可以在主线程也可以在子线程
- 接收广播在哪个线程要看是异步广播还是同步广播。如果是异步广播,无论发送广播是在主线程还是在子线程,内部都会通过Handler消息机制切换到主线程执行。如果是同步广播,发送广播的线程和接收广播的线程为同一线程,此种方式会导致线程阻塞,使用时需谨慎。
- 如果广播接收在主线程的话,注意不要进行耗时操作
- 通过广播传值要依赖于Intent的传值方式及要求
下表是一个简单总结
发送本地广播的线程 | 同步 or 异步 | 是否阻塞 | 接收本地广播的线程 |
---|---|---|---|
主线程 | 异步 | 否 | 主线程 |
主线程 | 同步 | 是 | 主线程 |
子线程 | 异步 | 否 | 主线程 |
子线程 | 同步 | 是 | 子线程 |
应用场景
单对单通信
场景描述:一个页面向另一个页面发送消息,可以是相邻页面也可以不相邻
实现方式:使用特定的action注册BroadcastReceiver,并使用具有该action的Intent发送本地广播
单对多通信
场景描述:一个页面发送广播,多个页面可以接收
实现方式:每个页面都使用相同的action注册BroadcastReceiver即可
多对单通信
场景描述:一个页面可以接收到多个不同广播
实现方式:该页面使用多个不同的action(一个IntentFilter可以有多个action)注册BroadcastReceiver,这样就能接收到具有不同action的广播了
应用示例
以下示例有3个页面,分别为ActivityA、ActivityB、ActivityC,其中ActivityA和ActivityB分别注册了广播接收器,ActivityC发送广播,ActivityA和ActivityB都可以接收到广播,接收到广播之后取出传值显示在UI上。
代码中注释了子线程发送广播、同步发送广播等逻辑,同时还有Log输出,可以查看同步、异步的执行顺序以及发送广播和接收广播所在的线程信息。源代码很简单,如下。
OnReceiveBrodadcastListener接口
public interface OnReceiveBroadcastListener void onReceive(Context context, Intent intent);
LocalBroadcastReceiver类,继承自BroadcastReceiver
public class LocalBroadcastReceiver extends BroadcastReceiver private OnReceiveBroadcastListener mListener; @Override public void onReceive(Context context, Intent intent) if (mListener != null) mListener.onReceive(context, intent); public LocalBroadcastReceiver(OnReceiveBroadcastListener listener) mListener = listener;
ActivityA的实现
public class ActivityA extends AppCompatActivity private static final String TAG = "LocalBroadcastManager"; private TextView tv_a; private Button btn_a; private LocalBroadcastReceiver mReceiver; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); Log.d(TAG, "main thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); tv_a = (TextView) findViewById(R.id.tv_a); btn_a = (Button) findViewById(R.id.btn_a); btn_a.setOnClickListener(new View.OnClickListener() @Override public void onClick(View v) Intent intent = new Intent(ActivityA.this, ActivityB.class); startActivity(intent); ); mReceiver = new LocalBroadcastReceiver(new OnReceiveBroadcastListener() @Override public void onReceive(Context context, Intent intent) // 注意:异步消息是在主线程处理的,务必避免耗时操作 // 如果接收多个广播,可以根据action分别处理 String action = intent.getAction(); Log.d(TAG, "A onReceive=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); Log.d(TAG, "A received broadcast,action=" + action); // 取出广播传值 String name = intent.getStringExtra("NAME"); tv_a.setText(name); ); IntentFilter filter = new IntentFilter(); // 一个IntentFilter可以配置多个action filter.addAction("com.aspook.localbroadcast_A"); filter.addAction("com.aspook.localbroadcast_AA"); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); // 同一个BroadcastReceiver可以注册多次,极端情况下,全局可以只有一个 // IntentFilter filter2 = new IntentFilter(); // filter2.addAction("com.aspook.localbroadcast_AAA"); // LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter2); @Override protected void onDestroy() super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
ActivityB的实现
public class ActivityB extends AppCompatActivity private static final String TAG = "LocalBroadcastManager"; private TextView tv_b; private Button btn_b; private LocalBroadcastReceiver mReceiver; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_b); tv_b = (TextView) findViewById(R.id.tv_b); btn_b = (Button) findViewById(R.id.btn_b); btn_b.setOnClickListener(new View.OnClickListener() @Override public void onClick(View v) Intent intent = new Intent(ActivityB.this, ActivityC.class); startActivity(intent); ); mReceiver = new LocalBroadcastReceiver(new OnReceiveBroadcastListener() @Override public void onReceive(Context context, Intent intent) Log.d(TAG, "B onReceive=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); Log.d(TAG, "B received broadcast,action=" + intent.getAction()); Bundle bundle = intent.getExtras(); int age = bundle.getInt("AGE"); tv_b.setText(age + ""); ); IntentFilter filter = new IntentFilter(); // 允许定义一个action数组,以接收不同的消息 filter.addAction("com.aspook.localbroadcast_B"); filter.addAction("com.aspook.localbroadcast_BB"); filter.addAction("com.aspook.localbroadcast_AA"); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); @Override protected void onDestroy() super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
ActivityC的实现
public class ActivityC extends AppCompatActivity private static final String TAG = "LocalBroadcastManager"; private TextView tv_c; private Button btn_c; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_c); tv_c = (TextView) findViewById(R.id.tv_c); btn_c = (Button) findViewById(R.id.btn_c); btn_c.setOnClickListener(new View.OnClickListener() @Override public void onClick(View v) // 在主线程发送本地广播 Log.d(TAG, "send broadcast=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); Intent intent = new Intent(); // Intent传值示例 intent.putExtra("NAME", "kevin"); Bundle bundle = new Bundle(); bundle.putInt("AGE", 22); intent.putExtras(bundle); intent.setAction("com.aspook.localbroadcast_AA"); // 异步执行 LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); // 同步执行 //LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcastSync(intent); Log.d(TAG, "do other things after localbroadcast!!!"); // 在子线程发送本地广播 // new Thread(new Runnable() // @Override // public void run() // Log.d(TAG, "send broadcast=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); // // Intent intent = new Intent(); // intent.putExtra("NAME", "kevin"); // Bundle bundle = new Bundle(); // bundle.putInt("AGE", 22); // intent.putExtras(bundle); // // action should defined as message constant // intent.setAction("com.aspook.localbroadcast_AAA"); // // 异步执行 // LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); // // 同步执行 // //LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcastSync(intent); // Log.d(TAG, "do other things after localbroadcast!!!"); // // ).start(); ); @Override protected void onDestroy() super.onDestroy();
上面代码只是一个本地广播的应用演示,具体使用时可以再进一步抽象封装,可用作应用内的通信机制。
以上是关于LocalBroadcastManager原理分析及应用的主要内容,如果未能解决你的问题,请参考以下文章
Android 之LocalBroadcastManager原理简析
LocalBroadcastManager 的实现原理,Handler还是 Binder?
LocalBroadcastManager 的源码分析及使用方法梳理
LocalBroadcastManager 的源码分析及使用方法梳理