LocalBroadcastManager是Android Support v4包提供的一个工具,用来在同一个应用内的不同组件间发送Broadcast的。相对 BroadcastReceiver,它只能用于应用内通信,安全性更好,同时拥有更高的运行效率。
注意,这是同一个应用,也就是其他应用就算和你共进程,也是无法互相发送广播的。因此可以确保隐私数据不会泄露。
那既然是同一个应用,为什么还要多此一举使用LocalBroadcast呢,直接调用不可以吗?使用本地广播可以让代码组织性更好,耦合性更低。
我们知道BroadcastReceiver 的通信是通过 Binder 机制的,而 LocalBroadcastManager 因为叫LocalBroadcast,可能让人产生一种它也是以 Binder 通讯方式为底层实现的错觉。查看源码,我们会发现这个更安全高效的实现机制原来如此简单。
1.LocalBroadcastManager 的使用
LocalBroadcastManager 的使用跟一般 BroadcastReceiver 差别不大。
具体使用可看示例:
public class LocalBroadcastActivity extends Activity {
private Button btSend;
private TextView tvBroadcast;
private Context mContext;
private LocalBroadcastReceiver localReceiver;
private static final String ACTION_LOCAL_SEND = "action_local_send";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_localbroadcast);
btSend = (Button) findViewById(R.id.btSend);
tvBroadcast = (TextView) findViewById(R.id.tvBroadcast);
mContext = this;
btSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//发送广播(一般与广播接收者写在应用的不同组件中使用)
Intent intent = new Intent(ACTION_LOCAL_SEND);
intent.putExtra("msg", "I am a broadcast message.");
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
});
//注册广播接收者
localReceiver = new LocalBroadcastReceiver();
LocalBroadcastManager.getInstance(mContext).registerReceiver(localReceiver, new IntentFilter(ACTION_LOCAL_SEND));
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(localReceiver);
}
//定义广播接收者(与一般的BroadcastReceiver写法一样)
public class LocalBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(ACTION_LOCAL_SEND)){
String msg = intent.getStringExtra("msg");
tvBroadcast.setText(msg);
}
}
}
}
可以看出,LocalBroadcastManager 与 BroadcastReceiver 的使用步骤基本一致,具体使用方式在代码中进行了详细的注释。比较简单吧。
2.LocalBroadcastManager 源码分析
还是先整体看一下源码吧:
package android.support.v4.content;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
* Helper to register for and send broadcasts of Intents to local objects
* within your process. This has a number of advantages over sending
* global broadcasts with {@link android.content.Context#sendBroadcast}:
* <ul>
* <li> You know that the data you are broadcasting won't leave your app, so
* don't need to worry about leaking private data.
* <li> 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.
* <li> It is more efficient than sending a global broadcast through the
* system.
* </ul>
*/
public final class LocalBroadcastManager {
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();
}
}
private static class BroadcastRecord {
final Intent intent;
final ArrayList<ReceiverRecord> receivers;
BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
intent = _intent;
receivers = _receivers;
}
}
private static final String TAG = "LocalBroadcastManager";
private static final boolean DEBUG = false;
private final Context mAppContext;
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
= new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions
= new HashMap<String, ArrayList<ReceiverRecord>>();
private final ArrayList<BroadcastRecord> mPendingBroadcasts
= new ArrayList<BroadcastRecord>();
static final int MSG_EXEC_PENDING_BROADCASTS = 1;
private final Handler mHandler;
private static final Object mLock = new Object();
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);
}
}
};
}
/**
* 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) {
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);
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) {
ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
if (filters == null) {
return;
}
for (int i=0; i<filters.size(); i++) {
IntentFilter filter = filters.get(i);
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--;
}
}
if (receivers.size() <= 0) {
mActions.remove(action);
}
}
}
}
}
}
/**
* 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) {
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);
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
if (entries != null) {
if (debug) Log.v(TAG, "Action list: " + entries);
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;
}
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 {
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;
}
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
/**
* 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();
}
}
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);
}
}
}
}
}
LocalBroadcastManager 的实现代码并不多,只有不到300行,但具体是怎么实现的呢?
(1) 构造方法
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);
}
}
};
}
由单例实现,因而构造方法设为私有。静态的mInstance实例保证变量生命周期和应用程序进程一样,然后getInstance()方法里面使用同步方法,保证多线程环境下,单例模式不会出现问题。
值得注意的是在构造方法中基于主线程的 Looper 新建了一个 Handler,保证消息的发送和处理都是在主线程执行的,在handleMessage中会通过executePendingBroadcasts()调用接收器对广播的消息进行处理,具体可见后面对executePendingBroadcasts()的介绍。
(2) 注册接收者
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<String, ArrayList<ReceiverRecord>>();
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
synchronized (mReceivers) {
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);
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);
}
}
}
mReceivers 存储广播和过滤器信息,以BroadcastReceiver作为 key,IntentFilter链表作为 value。 mReceivers 是接收器和IntentFilter的对应表,主要作用是方便在unregisterReceiver()中取消注册时找到相关的内容,同时作为对象锁限制注册接收器、发送广播、取消接收器注册等几个过程的并发访问。
mActions 以Action为 key,注册这个Action的BroadcastReceiver链表为 value。mActions 的主要作用是方便在广播发送后快速得到可以接收它的BroadcastReceiver。
(3) 发送广播
public boolean sendBroadcast(Intent intent) {
synchronized (mReceivers) {
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();
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);
if (receiver.broadcasting) {
continue;
}
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;
}
先根据Action从mActions中取出ReceiverRecord列表,循环每个ReceiverRecord判断 filter 和 intent 中的 action、type、scheme、data、categoried 是否匹配,是的话则保存到 receivers 列表中,然后发送 what 为MSG_EXEC_PENDING_BROADCASTS的消息,通过 Handler 去处理。
另外,源码中还有一个sendBroadcastSync()方法,是发送广播的同步方法,会在发送广播之后等消息处理后才返回。
public void sendBroadcastSync(@NonNull Intent intent) {
if (sendBroadcast(intent)) {
executePendingBroadcasts();
}
}
(4) 消息处理
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);
}
}
}
}
该方法在Handler的handleMessage()方法中调用,用来处理接收到的消息。将mPendingBroadcasts转换为数组BroadcastRecord,循环每个receiver,然后调用其onReceive函数,这样便完成了广播的核心逻辑。
(5) 取消注册
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);
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--;
}
}
if (receivers.size() <= 0) {
mActions.remove(action);
}
}
}
}
}
}
从mReceivers和mActions中找到并移除相应元素。
总结:
(1) LocalBroadcastManager 的核心实现实际上是借助 Handler实现的,只是利用到了 IntentFilter 的 match 功能。如果 BroadcastReceiver 换成其他接口也无所谓,这里只是顺便利用了现成的类和概念而已。
(2)因为是使用 Handler 实现的,只能用于应用内通信,因此安全性更好,效率更高。