我们知道Android中的广播(Broadcast)主要用于应用间的通信,这种通信机制依赖于Binder通信机制及AMS的参与。
当我们想实现应用内部组件之间的一对多通信时,广播机制的效率和开销可能无法满足要求。
这个时候我们可以使用第三方提供的开源库,例如EventBus等,
也可以使用Android支持库提供的LocalBroadcastManager。
本篇博客主要记录一下LocalBroadcastManager的基本用法,
同时分析一下LocalBroadcastManager的源码,看看其功能实现的原理。
1、基本用法
我实现一个简单的场景:
APK中有两个Activity,第一个Activity利用LocalBroadcastManager注册广播接收器,点击界面按键后启动第二个Activity。
进入第二个Activity后,点击按键就会通过LocalBroadcastManager发送广播,然后结束该Activity。
如果第一个Activity注册的广播接收器收到广播,就弹出一个Toast进行提示。
整个APK的功能极其简单,但基本囊括了LocalBroadcastManager的主要接口。
第一个Activity的代码如下:
public class FirstActivity extends AppCompatActivity {
private LocalBroadcastReceiver mReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//创建一个BroadcastReceiver,与常规广播一样,自己实现子类即可
mReceiver = new LocalBroadcastReceiver();
//调用LocalBroadcastManager的接口进行注册,参数与常规Broadcast一致
LocalBroadcastManager.getInstance(this)
.registerReceiver(mReceiver, new IntentFilter("ZJTest"));
Button button = (Button)findViewById(R.id.first_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击按键后,启动第二个Activity
Intent i = new Intent();
i.setClass(getApplicationContext(), SecondActivity.class);
startActivity(i);
}
});
}
private class LocalBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//收到广播后,用Toast提示
Toast.makeText(context, "Receive Local Broadcast", Toast.LENGTH_LONG).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//利用LocalBroadcastManager的接口,进行反注册
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(mReceiver);
}
}
我们再来看看SecondActivity的代码:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button button = (Button) findViewById(R.id.second_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击按键后,利用LocalBroadcastManager的接口发送本地广播
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcast(new Intent("ZJTest"));
finish();
}
});
}
}
从上面的代码可以看出,LocalBroadcastManager的使用极其简单。
与常规Broadcast相比,就是将Context对象替换为LocalBroadcastManager即可。
2、源码分析
现在我们来看看LocalBroadcastManager相关的源码。
2.1 构造函数
我们首先看一下LocalBroadcast构造函数相关的代码:
.................
static final int MSG_EXEC_PENDING_BROADCASTS = 1;
private final Handler mHandler;
private static final Object mLock = new Object();
//静态变量
private static LocalBroadcastManager mInstance;
//获取LocalBroadcastManager的接口
public static LocalBroadcastManager getInstance(Context context) {
//很明显,这是单例模式的写法
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
private LocalBroadcastManager(Context context) {
mAppContext = context;
//容易看出,LocalBroadcastManager的构造函数创建了一个Handler
//Handler的使用的是主线程的消息队列
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//收到MSG_EXEC_PENDING_BROADCASTS,调用函数处理广播
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
.................
从LocalBroadcastManager的构造函数可以看出,该对象是进程唯一的,
且在进程的主线程中处理消息。
2.2 注册广播接收器
LocalBroadcastManager注册广播接收器的接口如下所示:
................
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
synchronized (mReceivers) {
//LocalBroadcastManager中定义了一个内部类ReceiverRecord
//保存IntentFilter和BroadcastReceiver
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
//mReceivers的类型为HashMap<BroadcastReceiver, ArrayList<IntentFilter>>
//一个BroadcastReceiver可以对应多个IntentFilter
ArrayList<IntentFilter> filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<IntentFilter>(1);
mReceivers.put(receiver, filters);
}
filters.add(filter);
//mActions的类型为HashMap<String, ArrayList<ReceiverRecord>>
//一个IntentFilter中可能包含多个Action
//可能有多个BroadcastReceiver监听了同一个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);
}
}
}
.............
从上述的注册接口的代码可以看出,LocalBroadcastManager在本地维护了BroadcastReceiver、IntentFilter和Action之间的关系。
使用普通广播时,这些信息都会交由AMS统一维护。
2.3 反注册广播接收器
根据上文注册广播接收器的代码,了解LocalBroadcastManager的数据结构后,反注册广播接收器的代码就很容易理解了:
................
public void unregisterReceiver(BroadcastReceiver receiver) {
synchronized (mReceivers) {
//一个BroadcastReceiver可能对应多个IntentFilter
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与当前BroadcastReceiver之间的关系
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);
}
}
}
}
}
}
2.4 发送广播
经过前文的铺垫后,我们终于可以看看重头戏了,即LocalBroadcastManager发送广播的流程:
..................
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();
.....................
//根据Action取出所有初步匹配的ReceiverRecord,其中包含IntentFilter和BroadcastReceiver
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
if (entries != null) {
...................
//receivers中保存最终匹配的ReceiverRecord
ArrayList<ReceiverRecord> receivers = null;
for (int i=0; i<entries.size(); i++) {
ReceiverRecord receiver = entries.get(i);
...................
//当一个receiver被加入到receivers时,就会将broadcasting置为true
//这里是避免重复加入
//目前自己没看懂这个标识的意义,感觉整个流程不会有重复的ReceiverRecord
if (receiver.broadcasting) {
..................
continue;
}
//利用IntentFilter的接口进行完整的匹配
int match = receiver.filter.match(action, type, scheme, data,
categories, "LocalBroadcastManager");
if (match >= 0) {
..................
if (receivers == null) {
receivers = new ArrayList<ReceiverRecord>();
}
//匹配成功后,将receiver加入到receivers中,并将broadcasting标志置为true
receivers.add(receiver);
receiver.broadcasting = true;
} else {
//打印log信息
...............
}
}
//完成上文的匹配后,此时receivers中保存了所有与当前Intent完全匹配的ReceiverRecord
if (receivers != null) {
for (int i=0; i<receivers.size(); i++) {
//将broadcasting重新置为false
//这样下一个Intent到来时,ReceiverRecord才有机会重新加入到receivers中
//注意到改变broadcasting的操作,均在一个synchronized块中,因此完全是顺序执行
//个人感觉,完全可以不需要broadcasting标志
receivers.get(i).broadcasting = false;
}
//最后,构造BroadcastRecord,并加入到mPendingBroadcasts中
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
//若主线程队列中没有MSG_EXEC_PENDING_BROADCASTS,则发送该消息
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
LocalBroadcastManager的sendBroadcast接口流程,简单来讲就是根据Intent的信息,匹配出对应的BroadcastReceiver,
然后用BroadcastReceiver构造出BroadcastRecord对象,并发送消息,触发主线程进行处理。
2.5 处理广播
前文在LocalBroadcastManager的构造函数中,我们已经看到了,主线程收到MSG_EXEC_PENDING_BROADCASTS后,
将调用executePendingBroadcasts函数进行处理:
...............
private void executePendingBroadcasts() {
//注意此处为true
while (true) {
BroadcastRecord[] brs = null;
synchronized (mReceivers) {
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
//保存要处理的BroadcastRecord
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
//清空mPendingBroadcasts
//因此,再下次while循环结束前
//若没有新数据加入到mPendingBroadcasts,就会退出循环
//如果在下面的for循环过程中,其它线程再次调用sendBroadcast
//并将数据加入到mPendingBroadcasts中,那么while循环将继续处理
mPendingBroadcasts.clear();
}
for (int i=0; i<brs.length; i++) {
BroadcastRecord br = brs[i];
for (int j=0; j<br.receivers.size(); j++) {
//回调BroadcastReceiver的onReceive函数
//从这里可以看出,无论在什么线程利用LocalBroadcastManager注册BroadcastReceiver
//BroadcastReceiver的onReceive函数均在主线程被回调
//这与普通广播的处理相似
br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
}
}
}
}
...............
2.6 sendBroadcastSync接口
最后,我们来看看LocalBroadcastManager提供的sendBroadcastSync接口:
public void sendBroadcastSync(Intent intent) {
//从前文的代码知道,均有与Intent匹配的BroadcastReceiver时
//sendBroadcast返回true,同时BroadcastReceiver对应的ReceiverRecord被加入到mPendingBroadcasts待处理
if (sendBroadcast(intent)) {
//executePendingBroadcasts就是处理mPendingBroadcasts的
executePendingBroadcasts();
}
}
因此,一旦调用了sendBroadcastSync接口发送广播,那么该广播被处理后(有匹配的BroadcastReceiver时),
sendBroadcastSync接口才会返回。
具体分为两种情况:
1、主线程正在调用executePendingBroadcasts时,其它线程调用sendBroadcastSync接口,
那么新的ReceiverRecord将被加入到mPendingBroadcasts中。
由于executePendingBroadcasts中的while循环,那么mPendingBroadcasts变为非空后,
其中的信息有可能再次被主线程处理,即BroadcastReceiver的onReceive函数被主线程调用。
2、主线程没有调用executePendingBroadcasts时,其它线程线程调用sendBroadcastSync接口,
那么executePendingBroadcasts将在其它线程中运行。
此时,BroadcastReceiver的onReceive函数将被其它线程调用。
例如:
修改FirstActivity和SecondActivity的代码:
public class FirstActivity extends AppCompatActivity {
MyHandlerThread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//在非主线程中注册BroadcastReceiver
mThread = new MyHandlerThread(this, "ZJTest");
mThread.start();
mThread.getLooper();
Button button = (Button)findViewById(R.id.first_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent();
i.setClass(getApplicationContext(), SecondActivity.class);
startActivity(i);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
mThread.quitSafely();
}
private class MyHandlerThread extends HandlerThread {
private LocalBroadcastReceiver mReceiver;
private Context mContext;
MyHandlerThread(Context context, String name) {
super(name);
mContext = context.getApplicationContext();
}
@Override
protected void onLooperPrepared() {
mReceiver = new LocalBroadcastReceiver();
//注册
LocalBroadcastManager.getInstance(mContext)
.registerReceiver(mReceiver, new IntentFilter("ZJTest"));
}
@Override
public boolean quitSafely() {
//反注册
LocalBroadcastManager.getInstance(mContext)
.unregisterReceiver(mReceiver);
return super.quitSafely();
}
}
private class LocalBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String name = Thread.currentThread().getName();
//打印线程名称
Toast.makeText(context, "MyName is " + name, Toast.LENGTH_LONG).show();
}
}
}
不论SecondActivity在主线程还是其它线程,调用sendBroadcast接口发送广播时,
Toast均会提示“MyName is main”。
但如果SecondActivity在其它线程调用sendBroadcastSync函数,例如:
public class SecondActivity extends AppCompatActivity {
private HandlerThread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button button = (Button) findViewById(R.id.second_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//线程名为“test”
mThread = new HandlerThread("test");
mThread.start();
Handler handler = new Handler(mThread.getLooper());
//线程中调用sendBroadcastSync
handler.post(new Runnable() {
@Override
public void run() {
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcastSync(new Intent("ZJTest"));
}
});
finish();
}
});
}
@Override
public void onDestroy() {
mThread.quit();
super.onDestroy();
}
}
此时,Toast提示为“MyName is test”(后台线程抛出Toast后结束,Toast不会主动消失,除非回收进程,这里仅作为测试)。
3、总结
分析了LocalBroadcastManager后,我们知道了其原理实际上是:
将AMS中关于广播的处理流程移植到了本地。
由进程独立完成广播相关组件信息的存储、匹配及接口回调。