Android面試收集錄2 Broadcast Receiver詳解
1.Broadcast Receiver廣播接收器簡單介紹
1.1.定義
- Broadcast Receiver(廣播接收器),屬於Android四大組件之一
- 在Android開發中,Broadcast Receiver的應用場景非常多。廣播,是一個全局的監聽器,屬於Android四大組件。
Android 廣播分為兩個角色:廣播發送者,廣播接收者。
1.2.作用
- 用於監聽/接收 應用發出的廣播消息,並做出響應。
- 應用場景
a.不同組件之間通信(包括應用內/不同應用之間)
b.與Android系統在特定情況下的通信(如當電話呼入時、網絡可用時)
c.多線程通信
1.3.實現原理
Android中的廣播使用了設計模式中的觀察者模式:基於消息的發布/訂閱事件模型。
因此,Android將廣播的發送者和接收者解耦,使得系統方便集成,更易擴展。
模型中有3個角色:
i.消息訂閱者(廣播接收者)
ii.消息發布者(廣播發布者)
iii.消息中心(AMS,即Activity Manager Service)
原理描述:
i.廣播接收者 通過Binder機制在AMS註冊
ii.廣播發送者通過Binder機制向AMS發送廣播
iii.AMS根據廣播發送者要求,在已註冊列表中,尋找合適的廣播接收者
尋找依據:IntentFilter/Permission
iv.AMS將廣播發送到合適的廣播接收者相應的消息循環隊列中
v.廣播接收者通過消息循環拿到此廣播,並回調onReceiver()
特別註意:
廣播發送者和廣播接收者的執行是異步的,發出去的廣播不會關心有無接收者接收,也不確定接收者到底是何時才能接收到;
2.Broadcast Receiver使用方法
具體使用流程如下:
接下來一步一步介紹上圖中開發者手動完成部分:
4.1.自定義廣播接收者BroadcastReceiver
- 繼承自BroadcastReceiver基類
- 必須復寫抽象方法onReceive()方法
i.廣播接收器接收到相應廣播後,會自動回調onReceive()方法。
ii.一般情況下,onReceive方法會涉及與其他組件之間的交互,如發送Notification,啟動Service等
iii.默認情況下,廣播接收器運行在UI線程,因此,onReceive方法不能執行耗時操作,否則將導致ANR。
- 代碼範例 mBroadcastReceiver.java
public class mBroadcastReceiver extends BroadcastReceiver { //接收到廣播後自動調用該方法 @Override public void onReceive(Context context, Intent intent) { //寫入接收廣播後的操作 } }
4.2.廣播接收器註冊
註冊的方式分為兩種:靜態註冊、動態註冊
靜態註冊:
在AndroidManifest.xml通過<receive>標簽聲明
屬性說明:
<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" > //用於指定此廣播接收器將接收的廣播類型 //本示例中給出的是用於接收網絡狀態改變時發出的廣播 <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
註冊示例:
<receiver //此廣播接收者類是mBroadcastReceiver android:name=".mBroadcastReceiver" > //用於接收網絡狀態改變時發出的廣播 <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
當此APP首次啟動時,系統會自動實例化mBroadcastReceiver類,並註冊到系統中。
動態註冊:
在代碼中通過調用Context的*registerReceiver()*方法進行動態註冊BroadcastReceiver,具體代碼如下:
@Override protected void onResume() { super.onResume(); //實例化BroadcastReceiver子類 & IntentFilter mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); //設置接收廣播的類型 intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE); //調用Context的registerReceiver()方法進行動態註冊 registerReceiver(mBroadcastReceiver, intentFilter); } //註冊廣播後,要在相應位置記得銷毀廣播 //即在onPause() 中unregisterReceiver(mBroadcastReceiver) //當此Activity實例化時,會動態將MyBroadcastReceiver註冊到系統中 //當此Activity銷毀時,動態註冊的MyBroadcastReceiver將不再接收到相應的廣播。 @Override protected void onPause() { super.onPause(); //銷毀在onResume()方法中的廣播 unregisterReceiver(mBroadcastReceiver); }
特別註意:
動態廣播最好在Activity的onResume()註冊,onPause()註銷。
原因:
對於動態廣播,有註冊就必然得有註銷,否則會導致內存泄漏。重復註冊,重復註銷也不允許。
兩種註冊方式的區別:
4.3.廣播發送者向AMS發送廣播
廣播的發送:
- 廣播是用”意圖(Intent)“標識
- 定義廣播的本質:定義廣播所具備的“意圖(Intent)”
- 廣播發送:廣播發送者將此廣播的”意圖“通過sendBroadcast()方法發送出去
廣播的類型:
廣播的類型主要分為5類:
- 普通廣播(Normal Broadcast)
- 系統廣播(System Broadcast)
- 有序廣播(Ordered Broadcast)
- 粘性廣播(Sticky Broadcast)
- App應用內廣播(Local Broadcast)
具體說明如下:
①. 普通廣播(Normal Broadcast)
即開發者自身定義intent的廣播(最常用)。發送廣播使用如下:
Intent intent = new Intent(); //對應BroadcastReceiver中intentFilter的action intent.setAction(BROADCAST_ACTION); //發送廣播 sendBroadcast(intent);
若被註冊了的廣播接收者中註冊時intentFilter的action與上述匹配,則會接收此廣播(即進行回調onReceive())。
如下mBroadcastReceiver則會接收上述廣播
<receiver //此廣播接收者類是mBroadcastReceiver android:name=".mBroadcastReceiver" > //用於接收網絡狀態改變時發出的廣播 <intent-filter> <action android:name="BROADCAST_ACTION" /> </intent-filter> </receiver>
若發送廣播有相應權限,那麽廣播接收者也需要相應權限
②. 系統廣播(System Broadcast)
- Android中內置了多個系統廣播:只要涉及到手機的基本操作(如開機、網絡狀態變化、拍照等等),都會發出相應的廣播
- 每個廣播都有特定的Intent - Filter(包括具體的action),Android系統廣播action如下:
系統操作 | action |
---|---|
監聽網絡變化 | android.net.conn.CONNECTIVITY_CHANGE |
關閉或打開飛行模式 | Intent.ACTION_AIRPLANE_MODE_CHANGED |
充電時或電量發生變化 | Intent.ACTION_BATTERY_CHANGED |
電池電量低 | Intent.ACTION_BATTERY_LOW |
電池電量充足(即從電量低變化到飽滿時會發出廣播 | Intent.ACTION_BATTERY_OKAY |
系統啟動完成後(僅廣播一次) | Intent.ACTION_BOOT_COMPLETED |
按下照相時的拍照按鍵(硬件按鍵)時 | Intent.ACTION_CAMERA_BUTTON |
屏幕鎖屏 | Intent.ACTION_CLOSE_SYSTEM_DIALOGS |
設備當前設置被改變時(界面語言、設備方向等) | Intent.ACTION_CONFIGURATION_CHANGED |
插入耳機時 | Intent.ACTION_HEADSET_PLUG |
未正確移除SD卡但已取出來時(正確移除方法:設置--SD卡和設備內存--卸載SD卡) | Intent.ACTION_MEDIA_BAD_REMOVAL |
插入外部儲存裝置(如SD卡) | Intent.ACTION_MEDIA_CHECKING |
成功安裝APK | Intent.ACTION_PACKAGE_ADDED |
成功刪除APK | Intent.ACTION_PACKAGE_REMOVED |
重啟設備 | Intent.ACTION_REBOOT |
屏幕被關閉 | Intent.ACTION_SCREEN_OFF |
屏幕被打開 | Intent.ACTION_SCREEN_ON |
關閉系統時 | Intent.ACTION_SHUTDOWN |
重啟設備 | Intent.ACTION_REBOOT |
註意:
當使用系統廣播時,只需要在註冊廣播接收者時定義相關的action即可,
並不需要手動發送廣播,當系統有相關操作時會自動進行系統廣播。
③. 有序廣播(Ordered Broadcast)
定義發送出去的廣播被廣播接收者按照先後順序接收,有序是針對廣播接收者而言的。
廣播接受者接收廣播的順序規則(同時面向靜態和動態註冊的廣播接受者)
i.按照Priority屬性值從大到小排序;
ii.Priority屬性相同者,動態註冊的廣播優先;
特點:
i.接收廣播按順序接收
ii.先接收的廣播接收者可以對廣播進行截斷,即後接收的廣播接收者不再接收到此廣播;
iii.先接收的廣播接收者可以對廣播進行修改,那麽後接收的廣播接收者將接收到被修改後的廣播
具體使用:
有序廣播的使用過程與普通廣播非常類似,差異僅在於廣播的發送方式:
sendOrderedBroadcast(intent);
④. App應用內廣播(Local Broadcast)
背景:
Android中的廣播可以跨App直接通信(exported對於有intent-filter情況下默認值為true)
沖突:
可能出現的問題
其他App針對性發出與當前App intent-filter相匹配的廣播,由此導致當前App不斷接收廣播並處理;
其他App註冊與當前App一致的intent-filter用於接收廣播,獲取廣播具體信息; 即會出現安全性 & 效率性的問題。
解決方案:
使用APP應用內廣播(Local Broadcast)
App應用內廣播可理解為一種局部廣播,廣播的發送者和接收者都同屬於一個App。
相比於全局廣播(普通廣播),App應用內廣播優勢體現在:安全性高 & 效率高
具體使用1-將全局廣播設置成局部廣播
註冊廣播時將exported屬性設置為false,使得非本App內部發出的此廣播不被接收;
在廣播發送和接收時,增設相應權限permission,用於權限驗證;
發送廣播時指定該廣播接收器所在的包名,此廣播將只會發送到此包中的App內與之相匹配的有效廣播接收器中。
通過intent.setPackage(packageName)指定報名
具體使用2 - 使用封裝好的LocalBroadcastManager類 使用方式上與全局廣播幾乎相同,
只是註冊/取消註冊廣播接收器和發送廣播時將參數的context變成了LocalBroadcastManager的單一實例
註意:
對於LocalBroadcastManager方式發送的應用內廣播,只能通過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);
⑤. 粘性廣播(Sticky Broadcast)
由於在Android5.0 & API 21中已經失效,所以不建議使用,在這裏也不作過多的總結。
3.參考文章
3.1.以上內容全部來自於以下網址
https://github.com/LRH1993/android_interview/blob/master/android/basis/broadcastreceiver.md
Android面試收集錄2 Broadcast Receiver詳解