Handler系列文章:java
BroadcastReceiver做爲android四大組件之一經常被人使用。但不管是普通廣播仍是有序廣播,都是系統全局廣播。即發送的廣播能夠被任何應用程序接收到,但同時咱們也能接收來自任何應用程序的廣播。android
爲了解決廣播安全性的問題,Google引入了一套本地廣播機制。使用本地廣播機制發送的廣播只能在應用中進行傳遞,而廣播接收器也只能接收來自應用內的廣播。數組
本地廣播的使用和普通廣播基本沒區別,主要是使用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.getInstance(this)獲取實例能夠看出,LocalBroadcastManager是一個單例管理類。bash
查看LocalBroadcastManager#getInstance()的源碼並沒有異樣,繼續查看構造方法:ide
#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的構造函數只作了兩件事:函數
一、存儲ApplicationContext的Context。oop
二、初始化一個Handler,並將其綁定在主線程。源碼分析
LocalBroadcastManager竟然初始化了一個Handler,莫非本地廣播也是經過Handler實現的?post
在查看註冊廣播監聽器、註銷廣播監聽器和發送本地廣播的源碼以前,須要先了解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<ReceiverRecord> receivers;
BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
intent = _intent;
receivers = _receivers;
}
}
複製代碼
從名字上ReceiverRecord-接收器記錄,BroadcastRecord-廣播器記錄,能夠很好的理解各種中變量的意義。
ReceiverRecord主要用於存儲註冊廣播接收器時,存儲註冊廣播時傳遞的參數:IntentFilter 和 BroadcastReceiver。
BroadcastRecord主要存儲廣播的intent和存儲IntentFilter符合該intent.action的ReceiverRecord對象。
#LocalBroadcastManager.java
//存儲廣播接收器和對應接收器記錄列表(ArrayList<LocalBroadcastManager.ReceiverRecord>)
//在以前的版本這裏value存儲的是ArrayList<IntentFilter>
//LocalBroadcastManager.ReceiverRecord中存有IntentFilter,因此存儲ArrayList<LocalBroadcastManager.ReceiverRecord>和ArrayList<IntentFilter>道理都同樣
private final HashMap<BroadcastReceiver, ArrayList<LocalBroadcastManager.ReceiverRecord>> mReceivers = new HashMap();
//String存儲action,表示一個action對應多個接收該action的接收器記錄(ReceiverRecord)
//(ReceiverRecord內部存儲廣播接收器和過濾規則)
private final HashMap<String, ArrayList<LocalBroadcastManager.ReceiverRecord>> mActions = new HashMap();
//與發送的廣播的Action匹配的ReceiverRecord集合
//即存儲了廣播接收器、廣播過濾規則 和 發送廣播時的Intent
private final ArrayList<LocalBroadcastManager.BroadcastRecord> mPendingBroadcasts = new ArrayList();
複製代碼
LocalBroadcastManager定義了3個集合,簡單的說就是:
若是看不懂,暫時放下,配合下面的源碼分析食用。
分別從廣播機制的註冊廣播監聽器、發送廣播、註銷廣播監聽器入手查看源碼。 先從註冊廣播監聽器入手:
#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<ReceiverRecord> 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<filter.countActions(); i++) {
//獲取action
String action = filter.getAction(i);
//獲取特定action對應的接收器記錄
ArrayList<ReceiverRecord> entries = mActions.get(action);
//若是本來沒有這個條action的記錄
if (entries == null) {
//則建立一個存進去
entries = new ArrayList<ReceiverRecord>(1);
mActions.put(action, entries);
}
//將此次註冊廣播監聽器生成的接收器記錄對象存儲到該action對應的接收器記錄(ReceiverRecord)器列表中。
entries.add(entry);
}
}
}
複製代碼
總的來講,LocalBroadcastManager#registerReceiver()作了三件事:
再查看註銷廣播接收器的源碼:
#LocalBroadcastManager.java
public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
//加鎖
synchronized (mReceivers) {
//移除該廣播接收器的IntentFilter規則
final ArrayList<ReceiverRecord> 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<filter.filter.countActions(); j++) {
//獲取其中一個action
final String action = filter.filter.getAction(j);
//依據該action獲取對應的ReceiverRecord列表,即獲取其對應的廣播接收器(BroadcastReceiver)列表
final ArrayList<ReceiverRecord> 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);
}
}
}
}
}
}
複製代碼
註銷廣播接收器和註冊廣播接收器作了相反的事情:
#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<String> categories = intent.getCategories();
//根據intent對象的action獲取其對應的接收器記錄(ReceiverRecord)列表
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
//若是該action的接收器記錄(ReceiverRecord)列表不爲空
if (entries != null) {
//..
//這個列表用來記錄實際用來接收該廣播的廣播接收器(BroadcastReceiver)
ArrayList<ReceiverRecord> receivers = null;
for (int i=0; i<entries.size(); i++) {
//獲取單個接收器記錄,即獲取單個廣播接收器
ReceiverRecord receiver = entries.get(i);
//..
//若是當前廣播接收器正在廣播,則先跳過,防止重複註冊
if (receiver.broadcasting) {
//..
continue;
}
//測試此過濾器是否與給定的意圖數據匹配。 只有當Intent中的操做和類別與過濾器匹配時,匹配纔會成功
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;
} else {
//..
}
}
//最後接受廣播的廣播接收器列表不爲空,則將intent對象傳遞進去。
if (receivers != null) {
for (int i=0; i<receivers.size(); i++) {
//恢復不是正在廣播的狀態
receivers.get(i).broadcasting = false;
}
//將該廣播的intent對象和廣播接收器列表封裝成接收器記錄,存儲在mPendingBroadcasts中。
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
//發送特定what的Message,交由Handler處理。 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
複製代碼
sendBroadcast()作了4個步驟:
一、獲取廣播的intent信息,用於後續的匹對。
二、經過廣播的action在mActions中獲取其對應的接收器記錄(ReceiverRecord)列表。
三、遍歷接收器記錄(ReceiverRecord)列表,將列表內元素的IntentFilter對象與廣播的intent對象進行匹對,若是匹對成功,則存儲在新的接收器記錄(ReceiverRecord)列表receivers中。
四、將新的接收器記錄(ReceiverRecord)列表receivers 和 廣播的intent對象存儲在廣播記錄(BroadcastRecord)對象中,並將該廣播記錄(BroadcastRecord)對象存儲在mPendingBroadcasts中。
五、發送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<brs.length; i++) {
final BroadcastRecord br = brs[i];
//遍歷接收器記錄列表,即廣播接收器列表
final int nbr = br.receivers.size();
for (int j=0; j<nbr; j++) {
final ReceiverRecord rec = br.receivers.get(j);
//廣播接收器列表沒有被標記失效,則進行onReceive()回調進行廣播。
if (!rec.dead) {
rec.receiver.onReceive(mAppContext, br.intent);
}
}
}
}
}
複製代碼
executePendingBroadcasts()將mPendingBroadcasts轉換爲數組,遍歷廣播記錄數組。對還生效的接收器對象進行onReceive()回調,進行廣播。
注:當廣播接收器移除時,ReceiverRecord#dead會被標記爲true,則標記爲無效。
再回看3個列表對象:
本地廣播其實也是使用handler機制,將廣播接收器與action、IntentFilter相關聯。由於Handler的緣由,本地廣播也就只能在該應用內接收。