BroadcastReceiver作为android四大组件之一常常被人使用。但无论是普通广播还是有序广播,都是系统全局广播。即发送的广播可以被任何应用程序接收到,但同时我们也能接收来自任何应用程序的广播。
为了解决广播安全性的问题,Google引入了一套本地广播机制。使用本地广播机制发送的广播只能在应用中进行传递,而广播接收器也只能接收来自应用内的广播。
LocalBroadcast简单使用
本地广播的使用和普通广播基本没区别,主要是使用LocalBroadcastManager进行广播发送、注册广播接收器和注销广播接收器。
#daqiActivity.java
//本地广播管理器
private LocalBroadcastManager mBroadcastManager;
//广播接收器
private daqiBroadcastReceiver mBroadcastReceiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//.....
//获取广播管理类实例
mBroadcastManager = LocalBroadcastManager.getInstance(daqiActivity.this);
//初始化intent拦截器
IntentFilter mIntentFilter = new IntentFilter();
mIntentFilter.addAction("com.daqi.demo.LOCAL_BROADCAST");
//初始化广播接收器
mBroadcastReceiver = new daqiBroadcastReceiver();
//注册广播接收器
mBroadcastManager.registerReceiver(mBroadcastReceiver,mIntentFilter);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.daqi.demo.LOCAL_BROADCAST");
//发送本地广播
mBroadcastManager.sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销广播接收器
mBroadcastManager.unregisterReceiver(mBroadcastReceiver);
}
private class daqiBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(daqiActivity.this,"本地广播",Toast.LENGTH_SHORT).show();
}
}
LocalBroadcastManager源码分析
从LocalBroadcastManager.getInstance(this)获取实例可以看出,LocalBroadcastManager是一个单例管理类。
查看LocalBroadcastManager#getInstance()的源码并无异样,继续查看构造方法:
#LocalBroadcastManager.java
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
synchronized (mLock) {
if (mInstance == null) {
//获取ApplicationContext的context
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的构造函数只做了两件事:
1、存储ApplicationContext的Context。
2、初始化一个Handler,并将其绑定在主线程。
LocalBroadcastManager居然初始化了一个Handler,莫非本地广播也是通过Handler实现的?
LocalBroadcastManager内部类
在查看注册广播监听器、注销广播监听器和发送本地广播的源码之前,需要先了解LocalBroadcastManager得两个内部类ReceiverRecord 和 BroadcastRecord。
#LocalBroadcastManager.java
//接收器记录
private static final class ReceiverRecord {
final IntentFilter filter;
final BroadcastReceiver receiver;
//标记是否正在处于广播状态
boolean broadcasting;
//标记该接收器记录是否有效
//false为有效,true为无效。
boolean dead;
ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
filter = _filter;
receiver = _receiver;
}
//....
}
//广播器记录
private static final class BroadcastRecord {
//发送广播时的Intent对象
final Intent intent;
//接收器记录列表
final ArrayList receivers;
BroadcastRecord(Intent _intent, ArrayList _receivers) {
intent = _intent;
receivers = _receivers;
}
}
从名字上ReceiverRecord-接收器记录,BroadcastRecord-广播器记录,可以很好的理解各类中变量的意义。
ReceiverRecord主要用于存储注册广播接收器时,存储注册广播时传递的参数:IntentFilter 和 BroadcastReceiver。
BroadcastRecord主要存储广播的intent和存储IntentFilter符合该intent.action的ReceiverRecord对象。
LocalBroadcastManager内部集合
#LocalBroadcastManager.java
//存储广播接收器和对应接收器记录列表(ArrayList)
//在之前的版本这里value存储的是ArrayList
//LocalBroadcastManager.ReceiverRecord中存有IntentFilter,所以存储ArrayList和ArrayList道理都一样
private final HashMap> mReceivers = new HashMap();
//String存储action,表示一个action对应多个接收该action的接收器记录(ReceiverRecord)
//(ReceiverRecord内部存储广播接收器和过滤规则)
private final HashMap> mActions = new HashMap();
//与发送的广播的Action匹配的ReceiverRecord集合
//即存储了广播接收器、广播过滤规则 和 发送广播时的Intent
private final ArrayList mPendingBroadcasts = new ArrayList();
LocalBroadcastManager定义了3个集合,简单的说就是:
- mReceivers 存储广播接收器(BroadcastReceiver)对象 和 其IntentFilter信息。
- mActions 存储单个action 和 监听该action的接收器记录(ReceiverRecord)列表
- mPendingBroadcasts 存储发送广播时携带的intent对象 和 符合该intent.action的IntentFilter。
如果看不懂,暂时放下,配合下面的源码分析食用。
分别从广播机制的注册广播监听器、发送广播、注销广播监听器入手查看源码。
先从注册广播监听器入手:
registerReceiver()
#LocalBroadcastManager.java
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
//加锁
synchronized (mReceivers) {
//传递进来的receiver广播接收器与filter广播拦截器会被封装在ReceiverRecord对象中。
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
//从mReceivers中获取有关该receiver广播接收器的接收器记录(ReceiverRecord)对象
ArrayList filters = mReceivers.get(receiver);
//如果没有获取到对应的记录(正常是null)
if (filters == null) {
//创建一个size为1的ArrayList,并将信息存储进去。
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
//将存储注册广播信息的内容存储到filters中
filters.add(entry);
//遍历注册时传递进来的IntentFilter的action列表
for (int i=0; i entries = mActions.get(action);
//如果原本没有这个条action的记录
if (entries == null) {
//则创建一个存进去
entries = new ArrayList(1);
mActions.put(action, entries);
}
//将这次注册广播监听器生成的接收器记录对象存储到该action对应的接收器记录(ReceiverRecord)器列表中。
entries.add(entry);
}
}
}
总的来说,LocalBroadcastManager#registerReceiver()做了三件事:
- 将注册广播监听器的信息:IntentFilter 和 BroadcastReceiver。封装在ReceiverRecord中。
- 为广播接收器添加IntentFilter,存储在mReceivers中。
- 为注册时IntentFilter中的action添加接收器记录(ReceiverRecord),存储在mActions中。
unregisterReceiver()
再查看注销广播接收器的源码:
#LocalBroadcastManager.java
public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
//加锁
synchronized (mReceivers) {
//移除该广播接收器的IntentFilter规则
final ArrayList filters = mReceivers.remove(receiver);
if (filters == null) {
return;
}
//遍历其存储的所有IntentFilter
for (int i=filters.size()-1; i>=0; i--) {
//取出其中一个ReceiverRecord对象,其实是获取其对应的IntentFilter
final ReceiverRecord filter = filters.get(i);
//将该接收器记录标志为失效(死亡)
filter.dead = true;
//遍历这个IntentFilter过滤规则的action
for (int j=0; j receivers = mActions.get(action);
//如果该action的接收器记录(ReceiverRecord)列表不为空
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);
}
}
//如果接收器记录(ReceiverRecord)列表移除后为空,即该action无对应的广播接收器,则将该action移除。
if (receivers.size() <= 0) {
mActions.remove(action);
}
}
}
}
}
}
注销广播接收器和注册广播接收器做了相反的事情:
- 在mReceivers中,移除广播接收器和其IntentFilter。
- 在mActions中,移除IntentFilter#action对应的接收器记录(ReceiverRecord),若action无对应的接收器记录(ReceiverRecord),则将action移除。
sendBroadcast()
#LocalBroadcastManager.java
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 categories = intent.getCategories();
//根据intent对象的action获取其对应的接收器记录(ReceiverRecord)列表
ArrayList entries = mActions.get(intent.getAction());
//如果该action的接收器记录(ReceiverRecord)列表不为空
if (entries != null) {
//..
//这个列表用来记录实际用来接收该广播的广播接收器(BroadcastReceiver)
ArrayList receivers = null;
for (int i=0; i= 0) {
//..
//如果存储实际用来接收该广播的广播接收器列表没初始化,则初始化。
if (receivers == null) {
receivers = new ArrayList();
}
//将该广播接收器存储到列表中,并设置为正在广播的状态。
receivers.add(receiver);
receiver.broadcasting = true;
} else {
//..
}
}
//最后接受广播的广播接收器列表不为空,则将intent对象传递进去。
if (receivers != null) {
for (int i=0; i
sendBroadcast()做了4个步骤:
1、获取广播的intent信息,用于后续的匹对。
2、通过广播的action在mActions中获取其对应的接收器记录(ReceiverRecord)列表。
3、遍历接收器记录(ReceiverRecord)列表,将列表内元素的IntentFilter对象与广播的intent对象进行匹对,如果匹对成功,则存储在新的接收器记录(ReceiverRecord)列表receivers中。
4、将新的接收器记录(ReceiverRecord)列表receivers 和 广播的intent对象存储在广播记录(BroadcastRecord)对象中,并将该广播记录(BroadcastRecord)对象存储在mPendingBroadcasts中。
5、发送what = MSG_EXEC_PENDING_BROADCASTS的Message,让Handler传递广播intent对象到对应的接收器中。
注:对列表receivers存储接收器记录过程中,将接收器记录中的broadcasting标记为true,可以防止重复注册广播接收器造成的多次广播。
重归Handler的handleMessage方法中:
#LocalBroadcastManager.java
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
what = MSG_EXEC_PENDING_BROADCASTS的Message,会调用executePendingBroadcasts进行处理。
#LocalBroadcastManager.java
void executePendingBroadcasts() {
while (true) {
final BroadcastRecord[] brs;
synchronized (mReceivers) {
//获取待广播的广播记录对象。
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}mPendingBroadcasts
//将广播记录BroadcastRecord对象存储在数组中,并清空mPendingBroadcasts对象。
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}//解锁
//遍历广播记录数组
for (int i=0; i
executePendingBroadcasts()将mPendingBroadcasts转换为数组,遍历广播记录数组。对还生效的接收器对象进行onReceive()回调,进行广播。
注:当广播接收器移除时,ReceiverRecord#dead会被标记为true,则标记为无效。
再回看3个列表对象:
- mReceivers 存储广播接收器(BroadcastReceiver)对象 和 其IntentFilter信息。
作用:注销广播接收器时,通过广播接收器对象,寻找到其IntentFilter列表信息,方便根据IntentFilter列表中的action信息在mActions中进行移除。 - mActions 存储单个action 和 监听该action的接收器记录(ReceiverRecord)列表。
作用:发送广播时,根据广播中intent对象的action,可以快速寻找到其对应接收的接收记录对象,再根据接收器对象的IntentFilter与广播中intent对象做匹配,寻找符合该广播intent对象条件的广播接收器对象。 - mPendingBroadcasts 存储发送广播时携带的intent对象 和 符合该intent.action的IntentFilter。
作用:短暂存储需要传递的广播intent对象和接收的该intent对象广播接收器。
总结:
本地广播其实也是使用handler机制,将广播接收器与action、IntentFilter相关联。因为Handler的原因,本地广播也就只能在该应用内接收。