前言:本文所寫的是博主的個(gè)人見解厢岂,如有錯(cuò)誤或者不恰當(dāng)之處,歡迎私信博主阳距,加以改正塔粒! 原文鏈接,demo鏈接
廣播簡(jiǎn)述
Android應(yīng)用程序可以發(fā)送或者接收來自 Android 系統(tǒng)和其他 Android 應(yīng)用程序的廣播消息筐摘,類似于發(fā)布訂閱設(shè)計(jì)模式卒茬。當(dāng)感興趣的事件發(fā)生時(shí),這些廣播被發(fā)送咖熟。例如扬虚,Android 系統(tǒng)在各種系統(tǒng)事件發(fā)生時(shí)發(fā)送廣播,比如系統(tǒng)啟動(dòng)或者設(shè)備開始充電等球恤。應(yīng)用程序還可以發(fā)送自定義的廣播,比如通知其他應(yīng)用可能感興趣的內(nèi)容(例如荸镊,一些新數(shù)據(jù)已被下載)咽斧。
應(yīng)用程序可以注冊(cè)接收特定的廣播,當(dāng)發(fā)送廣播時(shí)躬存,系統(tǒng)自動(dòng)將廣播路由到訂閱該特定類型廣播的應(yīng)用程序张惹。一般來說,廣播可以用作跨應(yīng)用程序和正常用戶流之外的消息傳遞系統(tǒng)岭洲。
系統(tǒng)廣播
當(dāng)發(fā)生各種系統(tǒng)事件時(shí),系統(tǒng)會(huì)自動(dòng)發(fā)送廣播宛逗,例如系統(tǒng)切換飛行模式時(shí),系統(tǒng)廣播被發(fā)送到所有接收訂閱事件的應(yīng)用程序盾剩。
廣播消息本身被包裹在一個(gè) Intent 對(duì)象的動(dòng)作字符串標(biāo)識(shí)(例如 android.intent.action.AIRPLANE_MODE )雷激。這個(gè) Intent 還可以包括附加到其額外字段中的附加信息替蔬。比如,"飛行模式" 的 Intent 包括一個(gè) boolean 額外指示是否 "飛行模式"屎暇。
系統(tǒng)廣播行為的完整列表承桥,請(qǐng)參閱 Android SDK 中的 BROADCAST_ACTIONS.TXT 文件。每個(gè)廣播行為都有與之關(guān)聯(lián)的常量字段根悼,例如常量 ACTION_AIRPLANE_MODE_CHANGED 的值為 android.intent.action.AIRPLANE_MODE凶异,每個(gè)廣播動(dòng)作的文檔都可以在其相關(guān)聯(lián)的常量字段中獲得。
注:系統(tǒng)廣播更改
Android 7 和更高的不再發(fā)送以下系統(tǒng)廣播挤巡。這種優(yōu)化影響所有的應(yīng)用程序剩彬,不僅那些針對(duì)Android 7。
- ACTION_NEW_PICTURE
- ACTION_NEW_VIDEO
針對(duì) Android 7 的應(yīng)用程序(API級(jí)別24)和更高的必須登記以下的廣播代碼注冊(cè)廣播接收器(BroadcastReceiver 矿卑,
IntentFilter)喉恋。在清單中聲明接收器不起作用。 - CONNECTIVITY_ACTION
注冊(cè)接收廣播
應(yīng)用程序可以通過兩種方式接收廣播:通過清單聲明的接收者和上下文注冊(cè)的接收者
- 清單聲明的接收器(靜態(tài)注冊(cè))
在清單中聲明廣播接收者粪摘,可以通過下面的步驟:
(1)在應(yīng)用程序的清單中指定 <receiver>
元素
<receiver
android:name=".DemoBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED"/>
</intent-filter>
</receiver>
intent-filter (意圖過濾器) 指定你的接收者訂閱的廣播操作瀑晒。
(2)繼承 BroadcastReceiver 并實(shí)現(xiàn) onReceive(Context,Intent) 方法徘意,請(qǐng)看下面的示例:
public class DemoBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "DemoBroadcastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
String log = sb.toString();
Log.d(TAG, log);
Toast.makeText(context, log, Toast.LENGTH_LONG).show();
}
}
當(dāng)應(yīng)用程序安裝時(shí)苔悦,系統(tǒng)包管理器注冊(cè)接受器,然后接收器將成為你的應(yīng)用程序的單獨(dú)入口椎咧,這意味著如果應(yīng)用程序當(dāng)前未運(yùn)行玖详,系統(tǒng)可以啟動(dòng)你的應(yīng)用程序并發(fā)送廣播。
系統(tǒng)將創(chuàng)建一個(gè)新的 BroadcastReceiver 組件對(duì)象來處理它接收的每個(gè)廣播勤讽。這個(gè)對(duì)象僅對(duì)調(diào)用 onReceive(Context蟋座,Intent)的時(shí)候有效。當(dāng)你在代碼從該方法返回脚牍,系統(tǒng)將認(rèn)為組件不再活動(dòng)向臀。
-
context 注冊(cè)接收者(動(dòng)態(tài)注冊(cè))
用context注冊(cè)接收器,可以通過以下幾個(gè)步驟:
(1) 創(chuàng)建 BroadcastReceiver 并實(shí)例化
BroadcastReceiver broadcastReceiver = new DemoBroadcastReceiver();
(2) 創(chuàng)建一個(gè) IntentFilter 并調(diào)用 registerReceiver( BroadcastReceiver , IntentFilter ) 來注冊(cè)接收器
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); this.registerReceiver(broadcastReceiver,intentFilter);
注意:要注冊(cè)本地廣播诸狭,請(qǐng)改用 LocalBroadcastManager.registerReceiver( BroadcastReceiver , IntentFilter )券膀。
如果context注冊(cè)有效時(shí),context注冊(cè)的接收者就可以接收廣播驯遇。例如你在 Activity 的 context 中注冊(cè)芹彬,只要 Activity 沒有被銷毀,就會(huì)收到廣播叉庐。如果是使用 ApplicationContext 進(jìn)行注冊(cè)舒帮,只要程序在運(yùn)行就可以收到廣播。
(3)停止接收廣播時(shí),可以調(diào)用 unregisterReceiver( android.content.BroadcastReceiver ) 進(jìn)行注銷玩郊。當(dāng)你不需要這個(gè)廣播時(shí)或者 context 不再有效時(shí)肢执,一定要注銷接收器⊥咭耍·
注意你在哪里注冊(cè)和注銷的接收者蔚万,比如,你在 Activity 的 onCreate( Bundle ) 中注冊(cè)一個(gè)接收者临庇,則應(yīng)該在 onDestory() 中注銷它反璃,防止接收器泄露;如果你在 onResume() 中注冊(cè)一個(gè)接收器假夺,你應(yīng)該在 onPause() 中注銷淮蜈,以防止多次注冊(cè)( 如果你不想在 Activity 暫停時(shí)接收廣播,可以做減少不必要的系統(tǒng)開銷 )已卷。不要在 onSaveInstanceState( Bundle ) 中注銷梧田,如果用戶在歷史棧中移回,則不會(huì)調(diào)用這個(gè)方法侧蘸。
進(jìn)程狀態(tài)的影響
BroadcastReceiver 的狀態(tài)(無論是否在運(yùn)行)會(huì)影響其包含進(jìn)程的狀態(tài)裁眯,這也可能反過來影響其被系統(tǒng)殺死的可能性。例如讳癌,當(dāng)進(jìn)程執(zhí)行一個(gè)接收器時(shí)(在其 onReceive() 方法中運(yùn)行代碼)穿稳,它被認(rèn)為是一個(gè)前臺(tái)進(jìn)程,除非內(nèi)存壓力極度大的時(shí)候晌坤,否則系統(tǒng)將保持其進(jìn)程的運(yùn)行逢艘。
然而,一旦你的代碼從 onREceive() 方法中返回, BroadcastReceiver 就不在活動(dòng)了骤菠。接收器的宿主進(jìn)程變得和運(yùn)行在這個(gè)進(jìn)程中的其它應(yīng)用程序組件一樣重要它改。如果該進(jìn)程只承載一個(gè) manifest-declared 接收器(應(yīng)用程序從來沒有或者最近沒有跟用戶進(jìn)行交互),然后從 onReceive() 返回時(shí)商乎,系統(tǒng)將認(rèn)為它的進(jìn)程是一個(gè)低優(yōu)先級(jí)的進(jìn)程央拖,很可能會(huì)殺死它,從而施放資源鹉戚,用于其它優(yōu)先級(jí)高的進(jìn)程鲜戒。
因此,不應(yīng)該在廣播接收器中執(zhí)行耗時(shí)的后臺(tái)線程崩瓤,在執(zhí)行 onReceive() 后,系統(tǒng)隨時(shí)可以殺死進(jìn)程以回收內(nèi)存踩官,這樣做會(huì)終止在該進(jìn)程中運(yùn)行生成的線程却桶。為了避免這種情況的發(fā)生,應(yīng)該調(diào)用 goAsync() 方法(如果你需要更多的時(shí)間來處理后臺(tái)線程中的廣播)或使用 JobScheduler 從接收器調(diào)度 JobService ,則系統(tǒng)會(huì)知道該進(jìn)程繼續(xù)執(zhí)行工作颖系。
下面的這段代碼顯示了一個(gè) BroadcastReceiver 使用 goAsync() 來標(biāo)記嗅剖,需要更多的時(shí)間來完成當(dāng)前的操作,并在完成后再 onReceiver() 嘁扼。如果你在 onReceive() 中需要的時(shí)間很長(zhǎng)信粮,導(dǎo)致 UI 線程錯(cuò)過了一幀( 16 ms ),可以使用上面的方法趁啸,在后臺(tái)線程繼續(xù)工作强缘。
public class DemoBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "DemoBroadcastReceiver";
@Override
public void onReceive(Context context, final Intent intent) {
final PendingResult pendingResult = goAsync();
AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
@Override
protected String doInBackground(String... params) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
String log = sb.toString();
Log.d(TAG, log);
// 必須調(diào)用 finish() ,以便 BroadcastReceiver 可以被回收不傅。
pendingResult.finish();
return log;
}
};
asyncTask.execute();
}
}
發(fā)送廣播
Android 提供了三種方法供應(yīng)用發(fā)送廣播:
sendOrderedBroadcast( Intent , String ) 方法將廣播發(fā)送到一個(gè)接收器旅掂。隨著每個(gè)接收器依次執(zhí)行,它可以將結(jié)果傳遞到下一個(gè)接收器访娶,也可以完全中止廣播商虐,使它不會(huì)傳給其他接收器。Android 可以控制運(yùn)行的命令接收器:匹配 intent-filter 的優(yōu)先級(jí)屬性崖疤;具備相同優(yōu)先級(jí)的接收器秘车,可以任意順序運(yùn)行
sendBroadcast( Intent ) 方法向所有接收器發(fā)送未確定順序的廣播,這被成為普通廣播劫哼。這種廣播更有效率叮趴,但是意味著接收器不能讀取來自其他接收器的結(jié)果,傳播從廣播接收的數(shù)據(jù)沦偎,或者中止廣播
LocalBroadcastManager.sendBroadcast 方法將廣播發(fā)送到同個(gè)應(yīng)用程序中的接收者疫向。如果你不需要發(fā)送跨應(yīng)用廣播,請(qǐng)使用本地廣播豪嚎。實(shí)施 效率更高(無需進(jìn)行跨進(jìn)程通信)搔驼,不需要擔(dān)心其他可以接收或者發(fā)送廣播的應(yīng)用程序之間的任何安全問題
下面的代碼片段演示了如何通過創(chuàng)建 Intent 并 調(diào)用sendBroadcast( Intent ) 來發(fā)送廣播:
Intent intent = new Intent();
intent.setAction("com.passershowe.broadcast.NOTIFICATION_DEMO");
intent.putExtra("data","Send a notice");
sendBroadcast(intent);
廣播消息被包裝在 Intent 對(duì)象中,Intent 動(dòng)作字符串必須提供應(yīng)用程序的 java 包名和唯一的廣播事件標(biāo)識(shí)侈询。你可以使用 putExtra( String , Bundle ) 附加信息到 Intent 中舌涨,還可以通過 Intent 調(diào)用 setPackage( String ) 將廣播限制在同一組織中的一組應(yīng)用程序。
注意:雖然 Intent 用于發(fā)送廣播和使用 startActivity( Intent ) 啟動(dòng) Activity 扔字,但這些行為是完全無關(guān)的囊嘉。廣播接收器無法看到或者捕獲用于開始 Activity 的 Intent ;同樣革为,當(dāng)你是廣播 Intent 時(shí)扭粱,是無法找到或啟動(dòng) Activity 。
廣播權(quán)限約束
Permissions(權(quán)限) 允許你將廣播限制為持有一定權(quán)限的應(yīng)用程序集震檩∽粮颍可以對(duì)廣播的發(fā)送方或接收方強(qiáng)制執(zhí)行限制蜓堕。
- 發(fā)送權(quán)限
當(dāng)你調(diào)用 sendBroadcast( Intent , String ) 或 sendOrderedBroadcast( Intent , String , BroadcastReceiver , Handle , int , String , Bundle ) 時(shí),可以指定權(quán)限參數(shù)博其。只有通過其清單中的標(biāo)簽請(qǐng)求許可的接收器才能接收廣播套才。例如,以下代碼發(fā)送廣播:sendBroadcast(new Intent("com.passershowe.broadcast.NOTIFY"), Manifest.permission.SEND_SMS);
要接收廣播慕淡,接收應(yīng)用程序必須請(qǐng)求如下所示的權(quán)限:
<uses-permission android:name="android.permission.SEND_SMS"/>
你可以指定一個(gè)現(xiàn)有的系統(tǒng)權(quán)限(如SEND_SMS)或使用 <permission>
元素定義自定義權(quán)限
注意:當(dāng)你的應(yīng)用程序注冊(cè)了自定義權(quán)限背伴,你必須在使用這個(gè)權(quán)限前舅踪,安裝它
-
接收權(quán)限
如果你在注冊(cè)廣播接收器(或者使用 registerReceiver( BroadcastReceiver , IntentFilter , String , Handle ) 或清單中的<receiver>
標(biāo)簽)中指定權(quán)限參數(shù)经宏,則只有使用<uses-permission>
標(biāo)簽在其清單中(隨后被授予許可是危險(xiǎn)的)可以向接收者發(fā)送 Intent例如,假設(shè)你的接收應(yīng)用程序具有清單聲明的接收器域庇,如下所示:
<receiver android:name=".DemoBroadcastReceiver" android:permission="android.permission.SEND_SMS"> <intent-filter> <action android:name="android.intent.action.AIRPLANE_MODE"/> </intent-filter> </receiver>
或者你的接收應(yīng)用程序具有 context 注冊(cè)的接收器儿普,如下所示:
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
然后崎逃,為了能夠向這些接收者發(fā)送廣播,發(fā)送應(yīng)用程序必須如下所示請(qǐng)求許可:
<uses-permission android:name="android.permission.SEND_SMS"/>