Android四大組件之BroadcastReceiver

前言

Hi,你們好,又雙見面啦,上一期咱們講了如何使用Activity,確定有很多小夥伴已經建立了屬於本身的FirstActivity,那麼這一期咱們主要爲你們介紹第二個重要組件BroadcastReceiver(廣播接收器)。做爲Android的四大組件之二,其應用場景很是多。下面,就詳細介紹下 BroadcastReceiver 的相關知識。java

1. 定義

BroadcastReceiver(廣播接收器)即廣播,是一個全局的監聽器。android

Android 廣播分爲兩個角色:廣播發送者廣播接受者安全

2. 做用

能夠監聽或接收應用 App 或系統發出的廣播消息,並作出響應。網絡

3. 應用場景

  1. 同一 App 內部的同一組件內的消息通訊(單個或多個線程之間);
  2. 同一 App 內部的不一樣組件之間的消息通訊(單個進程);
  3. 同一 App 具備多個進程的不一樣組件之間的消息通訊;
  4. 不一樣 App 之間的組件之間消息通訊;
  5. Android系統在特定狀況下與App之間的消息通訊,如:網絡變化、電池電量、屏幕開關等。

4. 實現原理

Android中的廣播使用了觀察者模式:基於消息的發佈 / 訂閱事件模型,將廣播的發送者接收者解耦,使得系統方便集成,更易擴展。異步

消息的事件模型中有三個角色:ide

  1. 消息訂閱者(廣播接收者)
  2. 消息發佈者(廣播發送者)
  3. 消息中心(AMS,即Activity Manager Service)

具體實現流程以下:this

  1. 廣播接收者BroadcastReceiver經過Binder機制向AMS中進行註冊;
  2. 廣播發送者經過binder機制向AMS發送廣播;
  3. AMS查找符合相應條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發送到BroadcastReceiver(通常狀況下是Activity)相應的消息循環隊列中;
  4. 消息循環執行拿到此廣播,回調 BroadcastReceiver 中的 onReceive() 方法。

注意:廣播發送者和廣播接受者的執行順序是異步的,發送者不會關心有無接收者及接收者是否接收。google

5. 使用步驟

5.1 自定義廣播接收者BroadcastReceiver
//繼承BroadcastReceiver
public class MyBroadcaseReceiver extends BroadcastReceiver {   
    //接收到廣播後,則自動調用該方法    
    @Override    
    public void onReceive(Context context, Intent intent) {
    }
}

繼承 BroadcastReceivre 基類,重寫 onReceive() 方法。廣播接收器接收到相應廣播後,會自動回調 onReceive() 方法,此方法中可與其餘組件進行交互,如發送通知、啓動服務等。spa

默認狀況下,廣播接收器運行在主線程中,因此,onReceive() 方法不能執行耗時操做,不然會致使 ANR 異常。.net

5.2 註冊廣播接收器

廣播接收器的註冊分爲兩種:靜態註冊、動態註冊。

靜態註冊:靜態註冊即在清單文件(AndroidManifest.xml)中爲 BroadcastReceiver 進行註冊,使用< receiver >標籤聲明,並在標籤內用 < intent-filter > 標籤設置過濾器。這種形式的 BroadcastReceiver 的生命週期伴隨着整個應用。若是這種方式處理的是系統廣播,那麼無論應用是否在運行,該廣播接收器都能接收到該廣播。

<receiver 
    android:enabled=["true" | "false"]
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:process="string" >
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

android:exported ——此 BroadcastReceiver 可否接收其餘 App 的發出的廣播,其默認值是由 receiver 中有無 intent-filter 決定的,若是有 intent-filter,默認值爲true,不然爲false。(一樣的,activity/service中的此屬性默認值同樣遵循此規則);
android:name —— 此 BroadcastReceiver 類名;
android:permission ——若是設置,具備相應權限的廣播發送方發送的廣播才能被此 BroadcastReceiver 所接收;
android:process —— BroadcastReceiver 運行所處的進程。默認爲 App 的進程。能夠指定獨立的進程(Android四大組件均可以經過此屬性指定本身的獨立進程)。

intent-filter/action ——用於指定此廣播接收器將接收的廣播類型,本示例中給出的是用於接收網絡狀態改變時發出的廣播。

註冊示例:

<receiver    
    android:name=".MyBroadcaseReceiver">    
    <intent-filter>        
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />    
    </intent-filter>
</receiver>

當此 App首次啓動時,系統會自動實例化 MyBroadcaseReceiver 類,並註冊到系統中。

注意:Android 7.0版本開始,對靜態註冊的廣播作了限制,致使靜態註冊失效。應用沒法使用清單註冊隱式廣播,仍然能夠在運行時動態註冊這些廣播,而且可使用清單註冊專門針對它們的顯式廣播。

具體可查看:https://developer.android.goo...

動態註冊:動態註冊 BroadcastReceiver 是在代碼中定義並設置好一個 IntentFilter 對象,而後在須要註冊的地方調用 Context.registerReceiver() 方法,調用 Context.unregisterReceiver() 方法取消註冊,此時就不須要在清單文件中註冊 Receiver 了。

@Override
protected void onResume() {    
    super.onResume();    
    //1.實例化MyBroadcaseReceiver    
    MyBroadcaseReceiver myBroadcaseReceiver = new MyBroadcaseReceiver();    
    //2.設置廣播類型    
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");    
    //3.動態註冊廣播    
    registerReceiver(myBroadcaseReceiver, intentFilter);
}

@Override
protected void onDestroy() {    
    super.onDestroy();    
    //銷燬在onResume()中註冊的廣播    
    unregisterReceiver(mBroadcastReceiver);
}

注意:對於動態廣播,有註冊必須得有註銷,否知會形成內存泄露,重複註冊、重複註銷也不容許。

Android 中全部與觀察者模式有關的設計中,一旦涉及到 register,一定在相應的時機須要 unregister。

5.3 廣播發送及廣播類型

廣播發送:廣播的「發送」與「接收」,表面上看是廣播做爲 Android 廣播機制中的實體,實際上這一實體自己是並非以所謂的」廣播「對象存在的,而是以」意圖「(Intent)去表示。定義廣播的定義本質,實際就是相應廣播」意圖「的定義過程,而後經過廣播發送者經過 sendBroadcast() 方法將此」意圖「發送出去。

廣播類型:根據廣播的發送方式,能夠將其分爲如下幾種類型

1.普通廣播(Normal Broadcast)

開發者自身定義 intent的廣播。發送廣播使用以下:

Intent intent = new Intent();
   //對應BroadcastReceiver中intentFilter的action
   intent.setAction("MY_BROADCAST_ACTION");
   //發送廣播
   sendBroadcast(intent);

被註冊了的廣播接收者中註冊時 intentFilter 的 action 與上述匹配,就會接收此廣播,並回調onReceive()。以下的 BroadcastReceiver 則會接收上述廣播:

<receiver 
       android:name=".MyBroadcastReceiver" >
       <intent-filter>
           <action android:name="MY_BROADCAST_ACTION" />
       </intent-filter>
   </receiver>

注意:若發送廣播有相應權限,那麼廣播接收者也須要相應權限

2.系統廣播(System Broadcast)

Android系統中內置了多個系統廣播,只要涉及到手機的基本操做,基本上都會發出相應的系統廣播。如:開機啓動,網絡狀態改變,拍照,屏幕關閉與開啓,電量不足等等。

每一個系統廣播都具備特定的 intent-filter,其中主要包括具體的 action,系統廣播發出後,將被相應的BroadcastReceiver 接收。

當使用系統廣播時,只需在註冊廣播接收者時定義相關的action便可,不須要手動發送廣播,當系統有相關操做時會自動進行系統廣播的發送。

3.有序廣播(Ordered Broadcast)

有序廣播中的「有序」是針對廣播接收者而言的,指的是發送出去的廣播被 BroadcastReceiver 按照前後順序進行接收。有序廣播的定義過程與普通廣播無異,只是其發送方式變爲:sendOrderedBroadcast(intent);

廣播接受者接收廣播的順序規則(同時面向靜態和動態註冊的廣播接受者):按照 Priority 屬性值從大-小排序,Priority屬性相同者,動態註冊的廣播優先。

特色:接收廣播按順序接收;先接收的廣播接收者能夠對廣播進行截斷,即後接收的廣播接收者再也不接收到此廣播;先接收的廣播接收者也能夠對廣播進行修改,那麼後接收的廣播接收者將接收到被修改後的廣播。固然,通常狀況下,不建議對有序廣播進行此類操做,尤爲是針對系統中的有序廣播。

4.App應用內廣播(Local Broadcast)

因爲 Android 中的廣播能夠跨 App 直接通訊(exported對於有intent-filter狀況下默認值爲true),可能會出現相應安全隱患

a. 其餘 App 針對性發出與當前 App intent-filter 相匹配的廣播,由此致使當前 App 不斷接收廣播並處理;

b. 其餘 App 註冊與當前 App 一致的 intent-filter 用於接收廣播,獲取廣播具體信息;即會出現安全性 & 效率性的問題。

解決方案

方案1:將全局廣播設置成局部廣播

a. 對於同一 App 內部發送和接收廣播,將 exported 屬性設置成false,使得非本 App 內部發出的此廣播不被接收;

b. 在廣播發送和接收時,都增長上相應的permission,用於權限驗證;

c. 發送廣播時,指定特定廣播接收器所在的包名,具體是經過 intent.setPackage(packageName) 指定,這樣此廣播將只會發送到此包中的 App 內與之相匹配的有效廣播接收器中。

方案2:使用App應用內廣播(LocalBroadcastManager類)

App應用內廣播可理解爲一種局部廣播,廣播的發送者和接收者都同屬於一個App。相比於全局廣播(普通廣播),App應用內廣播優點體如今:安全性高 & 效率高。

使用封裝好的 LocalBroadcastManager 類使用方式上與全局廣播幾乎相同,只是註冊/取消註冊廣播接收器和發送廣播時將參數的 context 變成了 LocalBroadcastManager 的單一實例。

注意:對於LocalBroadcastManager方式發送的應用內廣播,只能經過LocalBroadcastManager動態註冊,不能靜態註冊。

//註冊應用內廣播接收器
   //1:實例化MyBroadcaseReceiver 
   MyBroadcaseReceiver myBroadcaseReceiver = new MyBroadcaseReceiver(); 
   //2:實例化IntentFilter、設置接收廣播的類型 
   IntentFilter intentFilter = new IntentFilter(); 
   intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
   //3:實例化LocalBroadcastManager
   LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
   //4:調用LocalBroadcastManager的registerReceiver()方法進行動態註冊 
   localBroadcastManager.registerReceiver(myBroadcaseReceiver, intentFilter);
   
   
   //取消註冊應用內廣播接收器
   localBroadcastManager.unregisterReceiver(myBroadcaseReceiver);
   
   
   //發送應用內廣播
   Intent intent = new Intent();
   intent.setAction("MY_BROADCAST_ACTION");
   localBroadcastManager.sendBroadcast(intent);

5.粘性廣播(Sticky Broadcast)

因爲在 Android 5.0 & API 21 中已經失效,因此不建議使用,在這裏不做闡述。

6. 特別注意

對於不一樣註冊方式的廣播接收器回調 onReceive(Context context,Intent intent)中的context返回值是不同的:

1.對於靜態註冊(全局+應用內廣播),回調 onReceive(context, intent) 中的 context 返回值是:ReceiverRestrictedContext

2.對於全局廣播的動態註冊,回調onReceive(context, intent)中的context返回值是:Activity Context

3.對於應用內廣播的動態註冊(LocalBroadcastManager方式),回調onReceive(context, intent)中的context返回值是:Application Context

4.對於應用內廣播的動態註冊(LocalBroadcastManager方式),回調onReceive(context, intent)中的context返回值是:Application Context

結語

做爲Android的四大組件之二,而且項目開發過程當中一些場景下常常被使用到,小夥伴們趕忙上手實操,把它靈活的運用到項目中,結合上一期的Activity實現有趣的交互吧。
PS:若是還有未看懂的小夥伴,歡迎加入咱們的QQ技術交流羣:892271582,裏面有各類大神回答小夥伴們遇到的問題哦~

相關文章
相關標籤/搜索