Android组件间通信之LocalBroadcastManager
Android开发中,大家都使用过全局广播(global broadcast)。
优点是操作简单,还可以进行跨进程通信。
缺点是全局广播涉及跨进程效率不高,而且因为全局广播发送其他应用也可以注册相应的监听,数据安全性得不到保证。
所以正因为使用的便携性所以少部分开发者会滥用广播,导致程序运行效率低及不可靠的安全性。
LocalBroadcastManager介绍
Android在V4包中给开发者提供了一个类LocalBroadcastManager,中文翻译是本地广播。他相较于全局广播有以下几点优势:
1. 你发送的广播只会在本应用(本进程)传播,不用担心会泄露隐私数据
2. 其他应用也不无法发送广播该类型广播至你的应用,所以不用担心安全性
3. 相比全局广播更加高效
4. 使用便捷,Api与全局广播一致
通过这些优点,我们可以发现LocalBroadcastManager很适合做应用内通信。
LocalBroadcastManager源码分析
我们一起来看看LocalBroadcastManager的源码
LocalBroadcastManager源码不多而且很简单,几个重要的方法:
LocalBroadcastManager#registerReceiver
LocalBroadcastManager#unregisterReceiver
LocalBroadcastManager#sendBroadcast
我们会发现LocalBroadcastManager其实是个单例,从这里就可以知道它只能在单一进程中使用。
public static LocalBroadcastManager getInstance(Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
LocalBroadcastManager#registerReceiver
代码不多,主要是做了以下件事:
1. 将传入的receiver与filter保存至内部类ReceiverRecord
2. 建立receiver与filter映射
3. 建立Action与ReceiverRecord间的映射,便于通过Action直接找到receiver
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
synchronized (mReceivers) {
// ReceiverRecord 内部类,保存了receiver与filter
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
ArrayList<IntentFilter> filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<IntentFilter>(1);
mReceivers.put(receiver, filters);
}
filters.add(filter);
// 建立Action与ReceiverRecord间的映射,便于通过Action直接找到receiver
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);
}
}
}
LocalBroadcastManager#unregisterReceiver
1. 反注册receiver后移除mReceivers中保存的receiver引用
2. 移除mActions中保存的对应action
public void unregisterReceiver(BroadcastReceiver receiver) {
synchronized (mReceivers) {
ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
if (filters == null) {
return;
}
for (int i=0; i<filters.size(); i++) {
IntentFilter filter = filters.get(i);
// filter对应多个action
for (int j=0; j<filter.countActions(); j++) {
String action = filter.getAction(j);
ArrayList<ReceiverRecord> receivers = mActions.get(action);
if (receivers != null) {
for (int k=0; k<receivers.size(); k++) {
if (receivers.get(k).receiver == receiver) {
receivers.remove(k);
k--;
}
}
// action对应的receiver为空时,移除mActions
if (receivers.size() <= 0) {
mActions.remove(action);
}
}
}
}
}
}
其实看到这里,就很明白了。这里的注册与反注册只是将receiver对象引用添加到LocalBroadcastManager的缓存队列中进行管理。是很典型的一种观察者模式。
接下来的套路应该就是收到改变通知订阅的观察者了,这也就是LocalBroadcastManager#sendBroadcast
的作用:
1. 发送方通过sendBroadcast通知订阅的观察者,传的Intent与普通广播一致
2. 解析intent参数,在队列中找到匹配的receiver 添加到待执行list
3. 通过handler发送消息方式异步执行回调
4. 遍历通知对应业务方
public boolean sendBroadcast(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();
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);
// 通过action获取到对应的receiver list
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
if (entries != null) {
if (debug) Log.v(TAG, "Action list: " + entries);
ArrayList<ReceiverRecord> receivers = null;
// 遍历receivers
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;
}
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>();
}
// 将匹配成功的receiver改变标志位并添加到List中
receivers.add(receiver);
receiver.broadcasting = true;
} else {
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) {
for (int i=0; i<receivers.size(); i++) {
receivers.get(i).broadcasting = false;
}
// 将符合条件的receiver添加到待执行列表
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
// 通过handler发送消息的方式将回调异步执行
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
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);
}
}
};
}
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];
// 遍历receiver,进行回调
for (int j=0; j<br.receivers.size(); j++) {
br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
}
}
}
}
LocalBroadcastManager重要的代码就这么多,即简单又很精妙。是学习观察者模式的一种好方式。
总结
这样LocalBroadcastManager源码我们就分析完了,看到这里很多人就会想:“这不就是个普通的观察者模式,每天都在写,有必要用这个么?”
其实我们很多人在开发中都会遇到这种情况,一些状态的变化需要及时通知其他业务方,一般就需要写一个观察者管理类,通知订阅的观察者。这样的管理类都要写一套维护观察者的add,remove,notify,工作量较大,而且业务类需要实现的回调也会变的太多。
我们可以直接使用系统提供的LocalBroadcastManager,统一管理,把维护观察者的职责交给它,提升开发工作效率,而且因为Api的一致性可以结合全局广播做到跨进程通信,何乐而不为呢?
当然这种模式有个缺陷就是不能传递非序列化对象,解析Intent较为繁琐。