一、定義&作用、分類、使用場景
1.定義
Broadcast在Android系統(tǒng)中應用的非常廣泛,BroadcastReceiver是四大組件之一,Android中我們要發(fā)送的廣播內(nèi)容是一個Intent,這個Intent中可以攜帶我們要傳送的數(shù)據(jù)。Broadcast可以實現(xiàn)不同程序間的數(shù)據(jù)傳輸與共享,只要和發(fā)送廣播action相同的廣播接收者,都可以接收到這個廣播,也就是說,發(fā)送一個廣播可以被很多廣播接收者接收,也就是說BroadCastReceiver可以通過監(jiān)聽其它應用程序發(fā)送的廣播接收傳遞過來的信息進而實現(xiàn)進程間的通信
2.分類
2.1 從發(fā)送方式來說:
Normal Broadcast(普通廣播):完全異步的,可以在同一時刻被所有接收者接收到,消息傳遞的效率比較高,并且無法中斷廣播的傳播,通常調(diào)用sendBroadcast(Intent)(Intent, String)方法發(fā)送
System Broadcast(系統(tǒng)廣播):發(fā)生各種事件時,系統(tǒng)自動發(fā)送
Ordered Broadcast(有序廣播):發(fā)送有序廣播后,廣播接收者將按預先聲明的優(yōu)先級依次接收Broadcast.優(yōu)先級高的先接收到廣播,而在其onRecevier()執(zhí)行過程中,廣播不會傳播到下一個接收者,此時當前的廣播接收者可以abortBroadcast()來阻止廣播繼續(xù)向下傳播。調(diào)用sendOrderedBroadcast(Intent, String)方法發(fā)送
'Sticky Broadcast'(粘性廣播):sendStickyBroadcast()來發(fā)送該類型的廣播消息,當粘性廣播發(fā)送后,最后一個粘性廣播會滯留在操作系統(tǒng)中,在粘性廣播發(fā)送后的一段時間里,如果有新的符合廣播的動態(tài)注冊廣播接收者,將會收到這個廣播消息。對于靜態(tài)注冊的廣播接收者來說,這個等同于普通廣播。已棄用(API 21)
LocalBroadcastManager本地廣播:只在自身App內(nèi)傳播,由LocalBroadcastManager完成
2.2 注冊方式
廣播的注冊方式來分,分為以下2種:
a.靜態(tài)注冊:通過<receiver></receiver>的形式在AndroidManifest.xml中注冊的廣播;
b.動態(tài)注冊:通過context. registerReceiver在程序中顯示注冊的廣播;
2.3隱式/顯示
與應用程序無直接關系的任何廣播都是隱式廣播,比如ACTION_PACKAGE_REPLACE是一個隱式廣播,因為他會通知手機上每個安裝的包。
同樣,與您的應用程序直接相關的任何廣播都是顯示廣播
3.場景
a..app全局監(jiān)聽:不同APP之間的組件之間的消息通信。在AndroidManifest中靜態(tài)注冊的廣播接收器,一般我們在收到該消息后,需要做一些相應的動作,而這些動作與當前App的組件,比如Activity或者Service的是否運行無關。
b.不同app之間的組件之間消息通信。
比如:Activity或者Service中使用registerReceiver()動態(tài)注冊的廣播接收器。比如網(wǎng)絡連接發(fā)生變化時,需要在當前Activity頁面給用戶一些UI 上的提示,或者將Service中的網(wǎng)絡請求任務暫停。
二、使用方式
1.注冊廣播
靜態(tài)注冊:通過在AndroidManifest清單文件中用<receive>進行注冊的,注冊完成就一直運行,靜態(tài)注冊的廣播即使Activity銷毀了,甚至進程被殺死了,還是可以收到廣播。
<receiver
android:name="com.xxxx.receiver.TestBroadcastReceiver"
android:enabled="true"
android:process=":push">
<intent-filter android:priority="0x7fffffff">
<!-- 系統(tǒng)廣播: 開屏 -->
<action android:name="android.intent.action.USER_PRESENT"/>
<!-- 系統(tǒng)廣播: 網(wǎng)絡切換 -->
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<!-- 系統(tǒng)廣播: 開機 -->
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<!-- 系統(tǒng)廣播: USB插拔 -->
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
<!-- 系統(tǒng)廣播: 電池 -->
<action android:name="android.intent.action.BATTERY_CHANGED"/>
</intent-filter>
</receiver>
動態(tài)注冊:跟隨Activity的生命周期,是在代碼中調(diào)用registerReceiver來進行注冊的,會隨著Actvity的銷毀而銷毀。
IntentFilter _Filter = new IntentFilter();
_Filter.setPriority(0x7fffffff);
//系統(tǒng)廣播: 開屏
_Filter.addAction(Intent.ACTION_USER_PRESENT);
//系統(tǒng)廣播: 開機
_Filter.addAction(Intent.ACTION_BOOT_COMPLETED);
//系統(tǒng)廣播: USB插拔
_Filter.addAction(Intent.ACTION_POWER_CONNECTED);
_Filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
//系統(tǒng)廣播: 電池信息變化
_Filter.addAction(Intent.ACTION_BATTERY_CHANGED);
//系統(tǒng)廣播: 網(wǎng)絡切換
_Filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.getApplicationContext().registerReceiver(mTestBroadcastReceiver, _Filter);
2.LocalBroadcastManager
LocalBroadcastManager是android support v4包里提供的一個組件,用來在應用內(nèi)發(fā)送廣播。
使用方法和和Broadcast基本一致,只需要在Broadcast相關的代碼前加上LocalBroadcastManager.getInstance(context)就行了。
發(fā)送廣播:
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
注冊廣播監(jiān)聽器:
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, filter);
注銷廣播監(jiān)聽器:
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
相比于發(fā)送全局廣播的sendBroadcast有很多優(yōu)點:
(1)廣播的數(shù)據(jù)不會離開本身的進程,所以不用擔心泄露私人數(shù)據(jù);
(2)其他應用程序不可能發(fā)廣播給你的應用,所以不用擔心有安全漏洞會被利用;
(3)相比于經(jīng)過系統(tǒng)的全局廣播更有效率。
與Handler進行比較:
通過線程內(nèi)的通信用Handler會更方便,所以這種LocalBroadcastManager也是比較少用,不過相比于Handler,LocalBroadcastManager的優(yōu)勢在于如果在通過線程內(nèi),多個對象要收到消息,LocalBroadcastReceiver發(fā)一次,而Handler則要發(fā)多次。
三、實現(xiàn)機制原理
1.設計模式
Android中的廣播使用了觀察者模式,模型為基于消息的發(fā)布/訂閱事件模型。
2.模型成員:
消息發(fā)布者(廣播發(fā)布者)
消息訂閱者(廣播接收者)
消息中心(AMS,Activity Manager Service,一個Android系統(tǒng)中極其重要!的成分,以后我們會詳細講解)
發(fā)布訂閱模式屬于廣義上的觀察者模式
前者時最常用的一種觀察者模式的實現(xiàn),且從解耦和重用角度上看更優(yōu)于典型的觀察者模式
發(fā)布訂閱模式加入消息中心,實現(xiàn)發(fā)布者和訂閱者的解耦:
在觀察者模式中,觀察者需要直接訂閱目標事件,在目標發(fā)出內(nèi)容改變的事件后,直接接收事件并作出響應。
在發(fā)布訂閱模式中,多了一個消息中心,一方面從發(fā)布者接收事件,另一方面向訂閱者發(fā)布事件,訂閱者需要從消息中心訂閱事件。以此避免發(fā)布者和訂閱者之間產(chǎn)生依賴關系。
3.實現(xiàn)流程
廣播接收者BroadcastReceiver通過Binder機制向AMS(Activity Manager Service)進行注冊;
廣播發(fā)送者通過binder機制向AMS發(fā)送廣播;
AMS查找符合相應條件(IntentFilter/Permission等)的BroadcastReceiver
AMS將廣播發(fā)送到上述符合條件的BroadcastReceiver相應的消息循環(huán)隊列中
BroadcastReceiver通過消息循環(huán)執(zhí)行拿到此廣播,回調(diào)BroadcastReceiver中的onReceive()方法。
廣播發(fā)送者和廣播接收者的執(zhí)行是異步的,發(fā)出去的廣播不會關心有無接收者接收,也不確定接收者到底是何時才能接收到。
四、補充
1.BroadcastReceiver的生命周期
1.動態(tài)注冊:存活周期是在Context.registerReceiver和Context.unregisterReceiver之間,BroadcastReceiver每次收到廣播都是使用注冊傳入的對象處理的。
2.靜態(tài)注冊:進程在的情況下,receiver會正常收到廣播,調(diào)用onReceive方法,生命周期只存活在onReveive函數(shù)中,此方法結束,BroadcastReceiver就銷毀了。進程不存在時,廣播相應的進程會被激活,Application.onCreate會被調(diào)用,再調(diào)用onReceive()
2.unregisterReceiver
Android中所有與觀察者模式有關的設計中,一旦涉及到register,必定在相應的時機需要unregister,防止內(nèi)存泄漏。因此,上例在onDestroy()回調(diào)需要unregisterReceiver(mBroadcastReceiver)。
3Android 7.0 更改
Android 7.0起,系統(tǒng)不再發(fā)送以下系統(tǒng)廣播:
ACTION_NEW_PICTURE
ACTION_NEW_VIDEO
針對Android 7.0 (API級別24)和更高版本的應用程序必須通過registerReceiver()注冊以下廣播。在AndroidManifest中聲明<receiver>起作用。
CONNECTIVITY_ACTION
4.Android 8.0
8.0起,應用無法在Manifest中注冊大部分隱式系統(tǒng)廣播(即,并非專門針對此應用的廣播),此意也是在于降低隨Android同時運行的應用增多,發(fā)生性能變差的幾率。
解決:
優(yōu)先使用動態(tài)注冊Receiver的方式,能動態(tài)注冊絕不使用Manifest注冊
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.xxx.receiver");
TheReceiver receiver = new TheReceiver();
registerReceiver(receiver, intentFilter);
如果一定要Manifest注冊,那么當發(fā)送廣播的時候,指定廣播接收者的包名,即發(fā)送顯式廣播
如果我們不想發(fā)顯式廣播(因為我們不知道有誰要收廣播),對方又不能動態(tài)注冊,只能靜態(tài)注冊(許多應用希望是被動喚醒),我們應該怎么辦呢?
解決辦法:
發(fā)送廣播的時候攜帶intent.addFlags(0x01000000); 即能讓廣播突破隱式廣播限制。