BroadcastReceiver總結

BroadcastReceiver屬於Android四大組件之一

可用於監聽應用發出的廣播消息,並做出相應

應用場景 :

  1. 不同組件之間通信(包括應用內 / 不同應用之間)
  2.  與 Android 系統在特定情況下的通信 如當電話呼入時、網絡可用時
  3. 多線程通信

一、實現原理

使用了設計模式中的觀察者模式:基於消息的發佈/訂閱事件模型。

 

  1. 廣播接收者 通過 Binder機制在 AMS 註冊

  2. 廣播發送者 通過 Binder 機制向 AMS 發送廣播

  3. AMS 根據 廣播發送者 要求,在已註冊列表中,尋找合適的廣播接收者

    尋找依據:IntentFilter / Permission

  4. AMS將廣播發送到合適的廣播接收者消息循環隊列中;

  5. 廣播接收者通過 消息循環 拿到此廣播,並回調 onReceive()

特別注意:廣播發送者 和 廣播接收者的執行是異步的,發出去的廣播不會關心有無接收者接收,也不確定接收者到底是何時才能接收到;

二、廣播接受者實現

繼承自BroadcastReceivre基類,複寫抽象方法onReceive()方法

  1. 廣播接收器接收到相應廣播後,會自動回調onReceive()方法

  2. 一般情況下,onReceive方法會涉及與其他組件之間的交互,如發送Notification、啓動service等

  3. 默認情況下,廣播接收器運行在UI線程,因此,onReceive方法不能執行耗時操作,超過10秒將導致ANR

  4. 不要在廣播接收器裏面開子線程,廣播接受器的聲明週期很短,很可能廣播接受器被銷燬了線程還沒執行完,正確的處理方法就是通過調用activity或者service處理業務

 

2.1靜態註冊

在AndroidManifest.xml裏通過 <receive> 標籤聲明

常用屬性:

    <receiver
        android:enabled=["true" | "false"]
        //此broadcastReceiver能否接收其他App的發出的廣播
        //默認值是由receiver中有無intent-filter決定的:如果有intent-filter,默認值爲true,否則爲false
        android:exported=["true" | "false"]
        //繼承BroadcastReceiver子類的類名
        android:name=".mBroadcastReceiver"
        //具有相應權限的廣播發送者發送的廣播才能被此BroadcastReceiver所接收;
        android:permission="string"
        //BroadcastReceiver運行所處的進程
        //默認爲app的進程,可以指定獨立的進程
        android:process="string" >
        //用於指定此廣播接收器將接收的廣播類型
        //本示例中給出的是用於接收網絡狀態改變時發出的廣播
        //這裏的priority取值是-1000到1000,值越大優先級越高
        <intent-filter android:priority = "777">
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>

2.2動態註冊

在代碼中通過調用

@Override
protected void onResume() {
    super.onResume();
    mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
    IntentFilter intentFilter = new IntentFilter();
    //設置接收廣播的類型
    intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
    registerReceiver(mBroadcastReceiver, intentFilter);

}

//註冊廣播後,要在相應位置記得銷燬廣播
@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mBroadcastReceiver);
}

對於動態廣播,有註冊就必然得有註銷,否則會導致內存泄露

重複註冊、重複註銷也不允許

2.3靜態註冊和動態註冊的區別

1、靜態註冊的廣播接收者一經安裝就常駐在系統之中,不需要重新啓動喚醒接收者;動態註冊的廣播接收者隨着應用的生命週期,由registerReceiver開始監聽,由unregisterReceiver撤銷監聽,如果應用退出後,沒有撤銷已經註冊的接收者應用應用將會報錯。

2、當廣播接收者通過intent啓動一個activity或者service時,如果intent中無法匹配到相應的組件。動態註冊的廣播接收者將會導致應用報錯 而靜態註冊的廣播接收者將不會有任何報錯,因爲自從應用安裝完成後,廣播接收者跟應用已經脫離了關係。

三、廣播類型

3.1普通廣播

應用在需要通知各個廣播接收者的情況下使用,如 開機啓動

使用方法:sendBroadcast()

Intent intent = new Intent("android.provider.Telephony.SMS_RECEIVED"); 
//通過intent傳遞少量數據
intent.putExtra("data", "finch"); 
// 發送普通廣播
sendBroadcast(Intent); 

普通廣播是完全異步的,可以在同一時刻(邏輯上)被所有接收者接收到,所有滿足條件的 BroadcastReceiver 都會執行其 onReceive() 方法。   

消息傳遞的效率比較高,並且無法中斷廣播的傳播。

3.2有序廣播

應用在需要有特定攔截的場景下使用,如黑名單短信、電話攔截。

使用方法:sendOrderedBroadcast(intent, receiverPermission);

receiverPermission :一個接收器必須持有對應的權限才能接收你的廣播。如果爲 null ,不加限制(一般都爲null)。

在有序廣播中,我們可以在前一個廣播接收者將處理好的數據傳送給後面的廣播接收者,也可以調用abortBroadcast()來終結廣播的傳播

public void onReceive(Context arg0, Intent intent) {
  //獲取上一個廣播的bundle數據
  Bundle bundle = getResultExtras(true);//true:前一個廣播沒有結果時創建新的Bundle;false:不創建Bundle
  bundle.putString("key", "777");
  //將bundle數據放入廣播中傳給下一個廣播接收者
  setResultExtras(bundle); 
  //終止廣播傳給下一個廣播接收者
  abortBroadcast();
}

按優先級接受廣播

3.3異步廣播

使用方法:sendStickyBroadcast()

發出的Intent當接收Activity(動態註冊)重新處於onResume狀態之後就能重新接受到其Intent

就是說sendStickyBroadcast發出的最後一個Intent會被保留,下次當Activity處於活躍的時候又會接受到它。

發這個廣播需要權限:

<uses-permission android:name="android.permission.BROADCAST_STICKY" />

卸載該廣播:removeStickyBroadcast(intent);

在卸載之前該intent會保留,接收者在可接收狀態都能獲得。

3.4異步有序廣播

使用方法:sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras):

這個方法具有有序廣播的特性也有異步廣播的特性;同時需要權限:

 <uses-permission android:name="android.permission.BROADCAST_STICKY" />

 3.5系統廣播

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

 3.6本地廣播

Android中的廣播默認可以跨App直接通信(exported對於有intent-filter情況下默認值爲true)

可能出現的問題:

  • 其他App針對性發出與當前App intent-filter相匹配的廣播,由此導致當前App不斷接收廣播並處理;
  • 其他App註冊與當前App一致的intent-filter用於接收廣播,獲取廣播具體信息; 即會出現安全性 & 效率性的問題。

解決方案 使用App應用內廣播(Local Broadcast)

  1. App應用內廣播可理解爲一種局部廣播,廣播的發送者和接收者都同屬於一個App。

  2. 相比於全局廣播(普通廣播),App應用內廣播優勢體現在:安全性高 & 效率高

具體使用1 - 將全局廣播設置成局部廣播

  1. 註冊廣播時將exported屬性設置爲false,使得非本App內部發出的此廣播不被接收;

  2. 在廣播發送和接收時,增設相應權限permission,用於權限驗證;

  3. 發送廣播時指定該廣播接收器所在的包名,此廣播將只會發送到此包中的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);

3.7 粘性廣播

由於在Android5.0 & API 21中已經失效,所以不建議使用,在這裏也不作過多的總結

四、總結

靜態廣播接收的處理器是由PackageManagerService負責,當手機啓動或者新安裝了應用的時候,PackageManagerService會掃描手機中所有已安裝的APP應用,將AndroidManifest.xml中有關注冊廣播的信息解析出來,存儲至一個全局靜態變量當中。

動態廣播接收的處理器是由ActivityManagerService負責,當APP的服務或者進程起來之後,執行了註冊廣播接收的代碼邏輯,即進行加載,最後會存儲在一個另外的全局靜態變量中。需要注意的是:這個並非是一成不變的,當程序被殺死之後,已註冊的動態廣播接收器也會被移出全局變量,直到下次程序啓動,再進行動態廣播的註冊,當然這裏面的順序也已經變更了一次。

廣播發出的時候,廣播接收者接收的順序如下:

1.當廣播爲普通廣播時,有如下的接收順序:

無視優先級 動態優先於靜態

同優先級的動態廣播接收器,先註冊的大於後註冊的

同優先級的靜態廣播接收器,先掃描的大於後掃描的

2.如果廣播爲有序廣播,那麼會將動態廣播處理器和靜態廣播處理器合併在一起處理廣播的消息,最終確定廣播接收的順序:

優先級高的先接收

同優先級的動靜態廣播接收器,動態優先於靜態

同優先級的動態廣播接收器,先註冊的大於後註冊的

同優先級的靜態廣播接收器,先掃描的大於後掃描的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章