文章目錄
第一章 四大組件
第四組件 BroadcastReceiver
(1)定義
廣播,是一個全局的監聽器,屬於Android四大組件之一,Android 廣播分爲兩個角色:廣播發送者、廣播接收者
(2)作用
監聽 / 接收 應用 App 發出的廣播消息,並 做出響應
(3)應用場景
3.1)Android不同組件間的通信(含 :應用內 / 不同應用之間)
3.2)多線程通信
3.3)與 Android 系統在特定情況下的通信。如:電話呼入時、網絡可用時
(4)實現原理
Android中的廣播使用了設計模式中的觀察者模式:基於消息的發佈 / 訂閱事件模型
Android將廣播的發送者 和 接收者 解耦,使得系統方便集成,更易擴展
模型中有3個角色:
1、消息訂閱者(廣播接收者)
2、消息發佈者(廣播發布者)
3、消息中心(AMS,即Activity Manager Service)
觀察者模式:
觀察者設計模式定義了對象間的一種一對多的組合關係,以便一個對象的狀態發生變化時,所有依賴於它的對象都得到通知並自動刷新。
(5)使用流程
5.1 自定義廣播接受者BroadcastReceiver
繼承BroadcastReceivre基類,必須複寫抽象方法onReceive()方法
1.廣播接收器接收到相應廣播後,會自動回調 onReceive() 方法
2.一般情況下,onReceive方法會涉及 與 其他組件之間的交互,如發送Notification、啓動Service等
3.默認情況下,廣播接收器運行在 UI 線程,因此,onReceive()方法不能執行耗時操作,否則將導致ANR
// 繼承BroadcastReceivre基類
public class mBroadcastReceiver extends BroadcastReceiver {
// 複寫onReceive()方法
// 接收到廣播後,則自動調用該方法
@Override
public void onReceive(Context context, Intent intent) {
//寫入接收廣播後的操作
}
}
如下爲監聽網絡狀態變化的廣播接收器
class NetworkChangeReceiver extends BroadcastReceiver{//廣播接收器類
@Override
public void onReceiver(Context context,Intent intent){
//這裏需要權限,需要在AndroidManifest.xml中進行網絡訪問權限申請:
//<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
ConnectivityManager connectionManager = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isAvailable()) {
//有網
Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
} else {
//無網
Toast.makeText(context, "network is unavailable",
Toast.LENGTH_SHORT).show();
}
}
}
5.2 廣播接收器註冊
註冊的方式分爲兩種:靜態註冊、動態註冊
(1)靜態註冊
使用:在AndroidManifest.xml裏通過標籤聲明
特點:常駐、不受任何組件的生命週期影響(應用程序關閉後,如果有信息廣播,程序依舊會被系統調用),耗電,佔內存
應用場景:需要時刻監聽廣播
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的發出的廣播
//默認值是由receiver中有無intent-filter決定的:如果有intent-filter,默認值爲true,否則爲false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//繼承BroadcastReceiver子類的類名(廣播具體標識)
android:name=".mBroadcastReceiver"
//具有相應權限的廣播發送者發送的廣播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver運行所處的進程
//默認爲app的進程,可以指定獨立的進程
//注:Android四大基本組件都可以通過此屬性指定自己的獨立進程
android:process="string" >
//用於指定此廣播接收器將接收的廣播類型
// IntentFilter翻譯成中文就是“意圖過濾器”,主要用來過濾隱式意圖。當用戶進行一項操作的時候,Android系統會根據配置的 “意圖過濾器” 來尋找可以響應該操作的組件,服務。
//本示例中給出的是用於接收網絡狀態改變時發出的廣播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
當此 App首次啓動時,系統會自動實例化mBroadcastReceiver類,並註冊到系統中。
(2)動態註冊
使用:在代碼中調用Context.registerReceiver()方法
特點:非常駐、靈活、跟隨組件的生命週期變化(組件結束,廣播結束。故在組件結束前,必須移除廣播接收器)
應用場景
2.1)源碼
// 選擇在Activity生命週期方法中的onResume()中註冊
@Override
protected void onResume(){
super.onResume();
// 1. 實例化BroadcastReceiver子類 & IntentFilter(意圖過濾器,接受廣播類型)
mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
// 2. 設置接收廣播的類型(網絡變化廣播)
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
// 3. 動態註冊:調用Context的registerReceiver()方法
registerReceiver(mBroadcastReceiver, intentFilter);
}
// 註冊廣播後,要在相應位置記得銷燬廣播
// 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 當此Activity實例化時,會動態將MyBroadcastReceiver註冊到系統中
// 當此Activity銷燬時,動態註冊的MyBroadcastReceiver將不再接收到相應的廣播。
@Override
protected void onPause() {
super.onPause();
//銷燬在onResume()方法中的廣播
unregisterReceiver(mBroadcastReceiver);
}
}
2.2)動態廣播最好在Activity 的 onResume()註冊、onPause()註銷。
- 動態廣播有註冊必須有註銷,否則會導致內存泄露;重複註冊,註銷也不允許。
- 在onResume()註冊、onPause()註銷是因爲onPause()在App死亡前一定會被執行,從而保證廣播在App死亡前一定會被註銷,從而防止內存泄露。
5.3 廣播發送者向AMS發送廣播
(1)廣播的發送
廣播 是 用”意圖(Intent)“標識,定義廣播的本質 = 定義廣播所具備的“意圖(Intent)”
廣播發送 = 廣播發送者 將此廣播的“意圖(Intent)”通過sendBroadcast()方法發送出去
(2)廣播類型
a)普通廣播(Normal Broadcast)
開發者自身定義 intent的廣播(最常用)。
1、發送者發送自定義廣播
Intent intent = new Intent();
//對應BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//發送廣播
sendBroadcast(intent);
2、接受者註冊廣播
<receiver
//此廣播接收者類是mBroadcastReceiver
android:name=".mBroadcastReceiver" >
//用於接收網絡狀態改變時發出的廣播
<intent-filter>
<action android:name="BROADCAST_ACTION" />
</intent-filter>
</receiver>
若被註冊了的廣播接收者中註冊時intentFilter的action與上述匹配,則會接收此廣播(即進行回調onReceive())。
b)系統廣播(System Broadcast)
Android中內置了多個系統廣播:只要涉及到手機的基本操作(如開機、網絡狀態變化、拍照等等),都會發出相應的廣播
每個廣播都有特定的Intent - Filter(包括具體的action),Android系統廣播action如下:(部分)
系統操作 Action
監聽網絡變化 android.net.conn.CONNECTIVITY_CHANGE
電池電量低 Intent.ACTION_BATTERY_LOW
屏幕鎖屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS
屏幕被關閉 Intent.ACTION_SCREEN_OFF
屏幕被打開 Intent.ACTION_SCREEN_ON
注:當使用系統廣播時,只需要在註冊廣播接收者時定義相關的action即可,並不需要手動發送廣播,當系統有相關操作時會自動進行系統廣播
c)有序廣播(Ordered Broadcast)
發送出去的廣播被廣播接收者按照先後順序接收(按照Priority屬性值從大-小排序)
sendOrderedBroadcast(intent);
有序廣播與無序廣播區別:
- 無序廣播
context.sendBroadcast(Intent)方法發送的廣播,不可被攔截,當然發送的數據,接收者是不能進行修改的。 - 有序廣播
context.sendOrderBroadcast(Intent)方法發送的廣播,可被攔截,而且接收者是可以修改其中要發送的數據,修改和添加都是可以的,這就意味着優先接收者對數據修改之後,下一個接收者接受的數據是上一個接收者已經修改了的。
d)本地廣播(Local Broadcast)
Android中的廣播可以跨App直接通信(exported對於有intent-filter情況下默認值爲true),App應用內廣播可理解爲一種局部廣播,廣播的發送者和接收者都同屬於一個App。
(1)將全局廣播設置爲局部廣播
(a)註冊廣播時將exported屬性設置爲false,使得非本App內部發出的此廣播不被接收;
(b)在廣播發送和接收時,增設相應權限permission,用於權限驗證;
(c)發送廣播時指定該廣播接收器所在的包名,此廣播將只會發送到此包中的App內與之相匹配的有效廣播接收器中。(通過intent.setPackage(packageName)指定報名)
(2) 使用封裝好的LocalBroadcastManager類
使用方式上與全局廣播幾乎相同,只是註冊/取消註冊廣播接收器和發送廣播時將參數的context變成了LocalBroadcastManager的單一實例
//註冊應用內廣播接收器
//步驟1:實例化BroadcastReceiver子類 & IntentFilter mBroadcastReceiver
mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//步驟2:實例化LocalBroadcastManager的實例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
//步驟3:設置接收廣播的類型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//步驟4:調用LocalBroadcastManager單一實例的registerReceiver()方法進行動態註冊
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消註冊應用內廣播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//發送應用內廣播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);
注:對於不同註冊方式的廣播接收器回調OnReceive(Context context,Intent intent)中的context返回值是不一樣的
(3) 本地廣播與全局廣播的差別
(3.1)定義
全局廣播BroadcastReceiver是針對應用間、應用與系統間、應用內部進行通信的一種方式
本地廣播LocalBroadcastReceiver僅在自己的應用內發送接收廣播,也就是隻有自己的應用能收到,數據更加安全廣播只在這個程序裏,而且效率更高。
(3.2)使用
全局廣播:
1)製作intent(可以攜帶參數)
2)使用sendBroadcast()傳入intent;
3)製作廣播接收器類繼承BroadcastReceiver重寫onReceive方法(或者可以匿名內部類啥的)
4)在java中(動態註冊)或者直接在Manifest中註冊廣播接收器(靜態註冊)使用registerReceiver()傳入接收器和intentFilter
5)取消註冊可以在OnDestroy()函數中,unregisterReceiver()傳入接收器
本地廣播:
1)LocalBroadcastReceiver不能靜態註冊,只能採用動態註冊的方式。
2)在發送和註冊的時候採用,LocalBroadcastManager的sendBroadcast方法和registerReceiver方法
(6)源碼角度分析廣播機制
(6.1)系統廣播(BoardcastReceiver)源碼分析
- 廣播接受者BoardcastReceiver,並重寫onReceive()方法,通過Binder 機制在AMS註冊
- 廣播發送者 通過Binder 機制向AMS發送廣播
- AMS根據廣播發送者要求,在已註冊列表中,尋找合適的廣播接收器(尋找依據:IntentFilter)並將廣播發送到合適的廣播接受者相應的消息循環隊列中
- 廣播接受者通過消息循環,拿到此廣播,並回調onReceive()方法。
其中廣播發送者與廣播接受者的執行是異步的,即廣播發送者不會關心有無接受者接收&也不確定接受者何時才能接收到。
(6.2)本地廣播(LocalBoardcastManager)源碼分析
1、LocalBroadcastManager源碼
(1)構造函數
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);
}
}
};
}
先看構造函數,單例實現因而私有化構造函數。
注意的是基於主線程的 Looper 新建了一個 Handler,handleMessage中會調用接收器對廣播的消息進行處理,也是 LocalBroadcastManager 的核心部分,具體見後面executePendingBroadcasts()介紹。
(2)註冊接收器
HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
= new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
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) {
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 (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 (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 是否 match(intentFilter的match機制),是的話則保存到receivers列表中,發送 what 爲MSG_EXEC_PENDING_BROADCASTS的消息,通過 Handler 去處理。
(4)消息處理
Java
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);
}
}
}
}
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);
}
}
}
}
以上爲消息處理的函數。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 實現的應用內的通信,自然安全性更好,效率更高。
2、LocalBroadcastManager總結
本地廣播發送的廣播只在自身app傳播。不必擔心隱私數據泄露。
其他app無法對該app發送廣播。不必擔心安全漏洞的利用。
本地廣播更加高效、安全。
- 高效:因爲它內部是通過Handler實現的,它的sendBroadcast()方法含義並非和系統的sendBroadcast()一樣,它的sendBroadcast()方法其實就是通過Handler發送了一個Message而已。
- 安全:既然它是通過Handler實現廣播發送的,那麼相比系統廣播通過Binder機制實現那肯定更加高效,同時使用Handler來實現,別的app無法向我們應用發送該廣播,而我們app內部發送的廣播也不會離開我們的app。
LocalBroadcast內部協作主要是靠兩個Map集合:mReceivers和mActions,當然還有一個List集合mPendingBroadcasts,這個主要存儲待接收的廣播對象。
3、LocalBroadcastManager使用
(1)自定義BroadcastReceiver子類LocalBroadcastReceiver
public class LocalBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
localMsg.setText(intent.getStringExtra(MSG_KEY));
}
}
(2)註冊接收器
LocalBroadcastReceiver localReceiver = new LocalBroadcastReceiver();
LocalBroadcastManager.getInstance(context).registerReceiver(localReceiver, new IntentFilter(ACTION_LOCAL_SEND));
(3)發送廣播
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_LOCAL_SEND));
(4)取消註冊
LocalBroadcastManager.getInstance(context).unregisterReceiver(localReceiver);