Android基礎(chǔ)之BroadcastReceiver全面解析

一、 定義

  • BroadcastReceiver(廣播接收器),屬于Android四大組件之一
  • 在Android開(kāi)發(fā)中,BroadcastReceiver的應(yīng)用場(chǎng)景非常多廣播,是一個(gè)全局的監(jiān)聽(tīng)器,屬于Android四大組件之一

Android 廣播分為兩個(gè)角色:廣播發(fā)送者、廣播接收者

二、 作用

  • 用于監(jiān)聽(tīng) / 接收 應(yīng)用發(fā)出的廣播消息,并做出響應(yīng)

  • 應(yīng)用場(chǎng)景
    a. 不同組件之間通信(包括應(yīng)用內(nèi) / 不同應(yīng)用之間)

    b. 與 Android 系統(tǒng)在特定情況下的通信

    如當(dāng)電話呼入時(shí)、網(wǎng)絡(luò)可用時(shí)

    c. 多線程通信

三、實(shí)現(xiàn)原理

  • Android中的廣播使用了設(shè)計(jì)模式中的觀察者模式:基于消息的發(fā)布/訂閱事件模型。

    因此,Android將廣播的發(fā)送者 和 接收者 解耦,使得系統(tǒng)方便集成,更易擴(kuò)展

  • 模型中有3個(gè)角色:

    1. 消息訂閱者(廣播接收者)
    2. 消息發(fā)布者(廣播發(fā)布者)
    3. 消息中心(AMS,即Activity Manager Service
  • 原理描述:

    1. 廣播接收者 通過(guò) Binder機(jī)制在 AMS 注冊(cè)

    2. 廣播發(fā)送者 通過(guò) Binder 機(jī)制向 AMS 發(fā)送廣播

    3. AMS 根據(jù) 廣播發(fā)送者 要求,在已注冊(cè)列表中,尋找合適的廣播接收者

      尋找依據(jù):IntentFilter / Permission

    4. AMS將廣播發(fā)送到合適的廣播接收者相應(yīng)的消息循環(huán)隊(duì)列中;

    5. 廣播接收者通過(guò) 消息循環(huán) 拿到此廣播,并回調(diào) onReceive()

特別注意:廣播發(fā)送者 和 廣播接收者的執(zhí)行 是 異步的,發(fā)出去的廣播不會(huì)關(guān)心有無(wú)接收者接收,也不確定接收者到底是何時(shí)才能接收到;

四、具體使用

具體使用流程如下:

接下來(lái)我將一步步介紹如何使用

即上圖中的 開(kāi)發(fā)者手動(dòng)完成部分

4.1 自定義廣播接收者BroadcastReceiver

  • 繼承自BroadcastReceivre基類

  • 必須復(fù)寫(xiě)抽象方法onReceive()方法

    1. 廣播接收器接收到相應(yīng)廣播后,會(huì)自動(dòng)回調(diào)onReceive()方法
    2. 一般情況下,onReceive方法會(huì)涉及與其他組件之間的交互,如發(fā)送Notification、啟動(dòng)service等
    3. 默認(rèn)情況下,廣播接收器運(yùn)行在UI線程,因此,onReceive方法不能執(zhí)行耗時(shí)操作,否則將導(dǎo)致ANR。
  • 代碼范例
    mBroadcastReceiver.java

public class mBroadcastReceiver extends BroadcastReceiver {

  //接收到廣播后自動(dòng)調(diào)用該方法
  @Override
  public void onReceive(Context context, Intent intent) {
    //寫(xiě)入接收廣播后的操作
    }
}

4.2 廣播接收器注冊(cè)

注冊(cè)的方式分為兩種:靜態(tài)注冊(cè)、動(dòng)態(tài)注冊(cè)

4.2.1 靜態(tài)注冊(cè)

  • 在AndroidManifest.xml里通過(guò) <receive> 標(biāo)簽聲明
  • 屬性說(shuō)明:
<receiver
  android:enabled=["true" | "false"]
  //此broadcastReceiver能否接收其他App的發(fā)出的廣播
  //默認(rèn)值是由receiver中有無(wú)intent-filter決定的:如果有intent-filter,默認(rèn)值為true,否則為false
  android:exported=["true" | "false"]
  android:icon="drawable resource"
  android:label="string resource"
  //繼承BroadcastReceiver子類的類名
  android:name=".mBroadcastReceiver"
  //具有相應(yīng)權(quán)限的廣播發(fā)送者發(fā)送的廣播才能被此BroadcastReceiver所接收;
  android:permission="string"
  //BroadcastReceiver運(yùn)行所處的進(jìn)程
  //默認(rèn)為app的進(jìn)程,可以指定獨(dú)立的進(jìn)程
  //注:Android四大基本組件都可以通過(guò)此屬性指定自己的獨(dú)立進(jìn)程
  android:process="string" >

  //用于指定此廣播接收器將接收的廣播類型
  //本示例中給出的是用于接收網(wǎng)絡(luò)狀態(tài)改變時(shí)發(fā)出的廣播
  <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
  </intent-filter>
</receiver>
  • 注冊(cè)示例

    <receiver
      //此廣播接收者類是mBroadcastReceiver
      android:name=".mBroadcastReceiver" >
      //用于接收網(wǎng)絡(luò)狀態(tài)改變時(shí)發(fā)出的廣播
      <intent-filter>
          <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
      </intent-filter>
    </receiver>
    

    當(dāng)此App首次啟動(dòng)時(shí),系統(tǒng)會(huì)自動(dòng)實(shí)例化mBroadcastReceiver類,并注冊(cè)到系統(tǒng)中。

4.2.2 動(dòng)態(tài)注冊(cè)

在代碼中通過(guò)調(diào)用Context的registerReceiver()方法進(jìn)行動(dòng)態(tài)注冊(cè)BroadcastReceiver,具體代碼如下:

@Override
protected void onResume() {
    super.onResume();

    //實(shí)例化BroadcastReceiver子類 &  IntentFilter
    mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
    IntentFilter intentFilter = new IntentFilter();

    //設(shè)置接收廣播的類型
    intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);

    //調(diào)用Context的registerReceiver()方法進(jìn)行動(dòng)態(tài)注冊(cè)
    registerReceiver(mBroadcastReceiver, intentFilter);
}


//注冊(cè)廣播后,要在相應(yīng)位置記得銷毀廣播
//即在onPause() 中unregisterReceiver(mBroadcastReceiver)
//當(dāng)此Activity實(shí)例化時(shí),會(huì)動(dòng)態(tài)將MyBroadcastReceiver注冊(cè)到系統(tǒng)中
//當(dāng)此Activity銷毀時(shí),動(dòng)態(tài)注冊(cè)的MyBroadcastReceiver將不再接收到相應(yīng)的廣播。
@Override
protected void onPause() {
    super.onPause();
    //銷毀在onResume()方法中的廣播
    unregisterReceiver(mBroadcastReceiver);
}

特別注意

  • 動(dòng)態(tài)廣播最好在Activity的onResume()注冊(cè)、onPause()注銷。

  • 原因:

    對(duì)于動(dòng)態(tài)廣播,有注冊(cè)就必然得有注銷,否則會(huì)導(dǎo)致內(nèi)存泄露

    重復(fù)注冊(cè)、重復(fù)注銷也不允許

4.2.3 兩種注冊(cè)方式的區(qū)別

4.3 廣播發(fā)送者向AMS發(fā)送廣播

4.3.1 廣播的發(fā)送

  • 廣播是用”意圖(Intent)“標(biāo)識(shí)
  • 定義廣播的本質(zhì):定義廣播所具備的“意圖(Intent)”
  • 廣播發(fā)送:廣播發(fā)送者將此廣播的”意圖“通過(guò)sendBroadcast()方法發(fā)送出去

4.3.2 廣播的類型

廣播的類型主要分為5類:

  • 普通廣播(Normal Broadcast)
  • 系統(tǒng)廣播(System Broadcast)
  • 有序廣播(Ordered Broadcast)
  • 粘性廣播(Sticky Broadcast)
  • App應(yīng)用內(nèi)廣播(Local Broadcast)

具體說(shuō)明如下:

①. 普通廣播(Normal Broadcast)

即開(kāi)發(fā)者自身定義intent的廣播(最常用)。發(fā)送廣播使用如下:

Intent intent = new Intent();
//對(duì)應(yīng)BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//發(fā)送廣播
sendBroadcast(intent);
  • 若被注冊(cè)了的廣播接收者中注冊(cè)時(shí)intentFilter的action與上述匹配,則會(huì)接收此廣播(即進(jìn)行回調(diào)onReceive())。如下mBroadcastReceiver則會(huì)接收上述廣播
<receiver 
    //此廣播接收者類是mBroadcastReceiver
    android:name=".mBroadcastReceiver" >
    //用于接收網(wǎng)絡(luò)狀態(tài)改變時(shí)發(fā)出的廣播
    <intent-filter>
        <action android:name="BROADCAST_ACTION" />
    </intent-filter>
</receiver>
  • 若發(fā)送廣播有相應(yīng)權(quán)限,那么廣播接收者也需要相應(yīng)權(quán)限

②. 系統(tǒng)廣播(System Broadcast)

  • Android中內(nèi)置了多個(gè)系統(tǒng)廣播:只要涉及到手機(jī)的基本操作(如開(kāi)機(jī)、網(wǎng)絡(luò)狀態(tài)變化、拍照等等),都會(huì)發(fā)出相應(yīng)的廣播
  • 每個(gè)廣播都有特定的Intent - Filter(包括具體的action),Android系統(tǒng)廣播action如下:
系統(tǒng)操作 action
監(jiān)聽(tīng)網(wǎng)絡(luò)變化 android.net.conn.CONNECTIVITY_CHANGE
關(guān)閉或打開(kāi)飛行模式 Intent.ACTION_AIRPLANE_MODE_CHANGED
充電時(shí)或電量發(fā)生變化 Intent.ACTION_BATTERY_CHANGED
電池電量低 Intent.ACTION_BATTERY_LOW
電池電量充足(即從電量低變化到飽滿時(shí)會(huì)發(fā)出廣播 Intent.ACTION_BATTERY_OKAY
系統(tǒng)啟動(dòng)完成后(僅廣播一次) Intent.ACTION_BOOT_COMPLETED
按下照相時(shí)的拍照按鍵(硬件按鍵)時(shí) Intent.ACTION_CAMERA_BUTTON
屏幕鎖屏 Intent.ACTION_CLOSE_SYSTEM_DIALOGS
設(shè)備當(dāng)前設(shè)置被改變時(shí)(界面語(yǔ)言、設(shè)備方向等) Intent.ACTION_CONFIGURATION_CHANGED
插入耳機(jī)時(shí) Intent.ACTION_HEADSET_PLUG
未正確移除SD卡但已取出來(lái)時(shí)(正確移除方法:設(shè)置--SD卡和設(shè)備內(nèi)存--卸載SD卡) Intent.ACTION_MEDIA_BAD_REMOVAL
插入外部?jī)?chǔ)存裝置(如SD卡) Intent.ACTION_MEDIA_CHECKING
成功安裝APK Intent.ACTION_PACKAGE_ADDED
成功刪除APK Intent.ACTION_PACKAGE_REMOVED
重啟設(shè)備 Intent.ACTION_REBOOT
屏幕被關(guān)閉 Intent.ACTION_SCREEN_OFF
屏幕被打開(kāi) Intent.ACTION_SCREEN_ON
關(guān)閉系統(tǒng)時(shí) Intent.ACTION_SHUTDOWN
重啟設(shè)備 Intent.ACTION_REBOOT

注:當(dāng)使用系統(tǒng)廣播時(shí),只需要在注冊(cè)廣播接收者時(shí)定義相關(guān)的action即可,并不需要手動(dòng)發(fā)送廣播,當(dāng)系統(tǒng)有相關(guān)操作時(shí)會(huì)自動(dòng)進(jìn)行系統(tǒng)廣播

③. 有序廣播(Ordered Broadcast)

  • 定義
    發(fā)送出去的廣播被廣播接收者按照先后順序接收

    有序是針對(duì)廣播接收者而言的

  • 廣播接受者接收廣播的順序規(guī)則(同時(shí)面向靜態(tài)和動(dòng)態(tài)注冊(cè)的廣播接受者)

    1. 按照Priority屬性值從大-小排序;
    2. Priority屬性相同者,動(dòng)態(tài)注冊(cè)的廣播優(yōu)先;
  • 特點(diǎn)

    1. 接收廣播按順序接收
    2. 先接收的廣播接收者可以對(duì)廣播進(jìn)行截?cái)啵春蠼邮盏膹V播接收者不再接收到此廣播;
    3. 先接收的廣播接收者可以對(duì)廣播進(jìn)行修改,那么后接收的廣播接收者將接收到被修改后的廣播
  • 具體使用
    有序廣播的使用過(guò)程與普通廣播非常類似,差異僅在于廣播的發(fā)送方式:

    sendOrderedBroadcast(intent);
    

④. App應(yīng)用內(nèi)廣播(Local Broadcast)

  • 背景
    Android中的廣播可以跨App直接通信(exported對(duì)于有intent-filter情況下默認(rèn)值為true)

  • 沖突
    可能出現(xiàn)的問(wèn)題:

    • 其他App針對(duì)性發(fā)出與當(dāng)前App intent-filter相匹配的廣播,由此導(dǎo)致當(dāng)前App不斷接收廣播并處理;
    • 其他App注冊(cè)與當(dāng)前App一致的intent-filter用于接收廣播,獲取廣播具體信息;
      即會(huì)出現(xiàn)安全性 & 效率性的問(wèn)題。
  • 解決方案
    使用App應(yīng)用內(nèi)廣播(Local Broadcast)

    1. App應(yīng)用內(nèi)廣播可理解為一種局部廣播,廣播的發(fā)送者和接收者都同屬于一個(gè)App。
    2. 相比于全局廣播(普通廣播),App應(yīng)用內(nèi)廣播優(yōu)勢(shì)體現(xiàn)在:安全性高 & 效率高
  • 具體使用1 - 將全局廣播設(shè)置成局部廣播

    1. 注冊(cè)廣播時(shí)將exported屬性設(shè)置為false,使得非本App內(nèi)部發(fā)出的此廣播不被接收;

    2. 在廣播發(fā)送和接收時(shí),增設(shè)相應(yīng)權(quán)限permission,用于權(quán)限驗(yàn)證;

    3. 發(fā)送廣播時(shí)指定該廣播接收器所在的包名,此廣播將只會(huì)發(fā)送到此包中的App內(nèi)與之相匹配的有效廣播接收器中。

      通過(guò) intent.setPackage(packageName) 指定報(bào)名

  • 具體使用2 - 使用封裝好的LocalBroadcastManager類
    使用方式上與全局廣播幾乎相同,只是注冊(cè)/取消注冊(cè)廣播接收器和發(fā)送廣播時(shí)將參數(shù)的context變成了LocalBroadcastManager的單一實(shí)例

    注:對(duì)于LocalBroadcastManager方式發(fā)送的應(yīng)用內(nèi)廣播,只能通過(guò)LocalBroadcastManager動(dòng)態(tài)注冊(cè),不能靜態(tài)注冊(cè)

//注冊(cè)應(yīng)用內(nèi)廣播接收器
//步驟1:實(shí)例化BroadcastReceiver子類 & IntentFilter mBroadcastReceiver 
mBroadcastReceiver = new mBroadcastReceiver(); 
IntentFilter intentFilter = new IntentFilter(); 

//步驟2:實(shí)例化LocalBroadcastManager的實(shí)例
localBroadcastManager = LocalBroadcastManager.getInstance(this);

//步驟3:設(shè)置接收廣播的類型 
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);

//步驟4:調(diào)用LocalBroadcastManager單一實(shí)例的registerReceiver()方法進(jìn)行動(dòng)態(tài)注冊(cè) 
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);

//取消注冊(cè)應(yīng)用內(nèi)廣播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);

//發(fā)送應(yīng)用內(nèi)廣播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);

⑤. 粘性廣播(Sticky Broadcast)

由于在Android5.0 & API 21中已經(jīng)失效,所以不建議使用,在這里也不作過(guò)多的總結(jié)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,940評(píng)論 25 709
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,953評(píng)論 2 59
  • 1、動(dòng)態(tài)注冊(cè)過(guò)程源碼分析: 在Activity中動(dòng)態(tài)注冊(cè)廣播室,在注冊(cè)方法之前其實(shí)省略了Context,也就是實(shí)際...
    騎著豬的蝸牛閱讀 847評(píng)論 0 1
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情況下的生命周期:在用戶參與的情況下...
    AndroidMaster閱讀 3,273評(píng)論 0 8
  • 文/白薰兒 考試周,上午十點(diǎn)半,舍友都還沒(méi)有起床。 而昨晚,十一點(diǎn)四十才手動(dòng)熄燈,最后一位舍友休息的時(shí)候,已經(jīng)到了...
    仙肚瑞拉閱讀 1,700評(píng)論 9 23

友情鏈接更多精彩內(nèi)容