分享一些 Broadcast 使用技巧

jeanboy發表於2019-05-08

簡介

Broadcast(廣播) 是 Android 的四大元件之一,用於程式/執行緒間通訊。

廣播最大的特點就是傳送方並不關心接收方是否接到資料,也不關心接收方是如何處理資料的,它只負責「說」而不管你「聽不聽」。

廣播可以來之系統,例如,Android 系統在發生各種系統事件時傳送廣播(系統啟動或者裝置開始充電時)。

也可以來自於其他應用程式,例如,應用程式也可以傳送自定義廣播,來通知其他應用程式接受他們可能感興趣的內容(更新資料)。

廣播的分類

按傳送方式分類

  • 標準廣播

是一種「完全非同步執行」的廣播,沒有任何先後順序,所有的廣播接收器幾乎同一時刻接收到這條廣播訊息,效率高,無法被截斷。

  • 有序廣播

是一種「同步執行」的廣播,有先後順序,同一時刻只有一個接收器可以接收這個廣播訊息,優先順序高的廣播接收器可以先收到廣播訊息,並且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣後面的廣播接收器就無法接收廣播訊息了。

按註冊方式分類

  • 靜態廣播

不管應用程式是否處於活動狀態,都會進行監聽。每次觸發都會建立新的 Receiver 物件。

  • 動態廣播

在程式碼中進行註冊,注意動態註冊的廣播一定要取消註冊才行,通常是在 onDestroy() 方法中呼叫 unregisterReceiver() 方法來實現。

從開始建立直到其被解除註冊會使用同一個 Receiver,無論這個廣播被觸發幾次。

按定義方式分類

  • 系統廣播

Android 系統中內建了多個系統廣播,每個系統廣播都具有特定的 IntentFilter,其中主要包括具體的 Action,系統廣播發出後,將被相應的 BroadcastReceiver 接收。系統廣播在系統內部當特定事件發生時,由系統自動發出。

  • 自定義廣播

由應用程式開發者自己定義的廣播。

按範圍方式分類

  • 全域性廣播

發出的廣播可以被其他任意的應用程式接收,或者可以接收來自其他任意應用程式的廣播。

  • 本地廣播

只能在應用程式的內部進行傳遞的廣播,廣播接收器也只能接收內部的廣播,不能接受其他應用程式的廣播。

廣播的使用

建立廣播接收器

使用廣播我們需要先建立 BroadcastReceiver(廣播接收器) ,直接繼承 BroadcastReceiver 建立子類並實現父類的 onReceive() 方法即可,如下示例程式碼。

public class MyReceiver extends BroadcastReceiver {
  // 自定義 action
  private static final String ACTION = "com.jeanboy.broadcast.MyReceiverFilter";
  
  @Override
  public void onReceive(Context context, Intent intent) {
    //TODO: 接收到廣播進行處理
  }
}
複製程式碼

靜態廣播

在使用廣播時還需要在 AndroidMainfest 檔案中定義,也就是註冊靜態廣播。

<receiver android:name=".ui.broadcast.MyReceiver"
          android:enabled="true"
          android:exported="true">
    <intent-filter>
        <!-- 例如:接收系統開機廣播 -->
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <!-- 例如:接收自定義的廣播 -->
        <action android:name="com.jeanboy.broadcast.MyReceiverFilter" />
    </intent-filter>
</receiver>
複製程式碼

上面的 enabled 設定為 true 意味著能夠接受到廣播資訊。exported 為 true 意味著能夠接收到外部 APK 傳送的廣播資訊。

動態廣播

使用動態廣播不需要在 AndroidMainfest 檔案中定義,只需在程式碼中註冊即可。

// 建立廣播
MyReceiver myReceiver = new MyReceiver();
// 建立 IntentFilter
IntentFilter intentFilter = new IntentFilter();
// 例如:新增系統廣播 action 接受網路變化
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
// 例如:新增自定義的 action
intentFilter.addAction(MyReceiver.ACTION);
// 註冊廣播
registerReceiver(myReceiver, intentFilter);
// 登出廣播
unregisterReceiver(myReceiver);
複製程式碼

傳送廣播

傳送廣播比較簡單,無論靜態廣播還是動態廣播,都是如下方式(系統廣播 Android 系統會自動傳送,不在本文討論範圍)。

// 建立 Intent
Intent intent = new Intent();
// 例如:新增自定義的 action
intent.setAction(MyReceiver.ACTION);
// 傳送廣播
sendBroadcast(intent);
複製程式碼

Android 8.0 中的靜態廣播

由於 Android 8.0 廢除大部分靜態廣播,對於程式碼需要修改某些部分。

傳送廣播部分需要設定 ComponetName

Intent intent = new Intent(MyReceiver.ACTION);
// ComponetName("自定義廣播的包名", "自定義廣播的路徑")
ComponentName component = new ComponentName("com.jeanboy.app.broadcast", "com.jeanboy.app.broadcast.MyReceiver");
intent.setComponent(component);
sendBroadcast(intent);
複製程式碼

帶許可權的廣播

使用廣播可能引發的安全問題:

  • 如果別的應用程式監聽我們的廣播,那麼會造成我們應用程式的資料洩露;
  • 如果別的應用程式冒充我們的應用傳送廣播,那麼就會頻繁的啟動我們的廣播接收程式,造成我們應用的混亂,甚至崩潰。

為了避免以上安全問題,Android 為我們提供了許可權機制。

首先在註冊靜態廣播時可以在 AndroidMainfest 檔案中新增許可權。

<manifest ...>
  <!-- 自定義一個自己的許可權 -->
  <permission android:name="com.jeanboy.permissions.MY_BROADCAST"/>
  <!-- 使用自定義的許可權 -->
  <uses-permission android:name="com.jeanboy.permissions.MY_BROADCAST"/>

  <application ...>
    <!-- 新增許可權 -->
    <receiver android:name=".ui.broadcast.MyReceiver"
              android:permission="com.jeanboy.broadcast.MY_BROADCAST"
              android:enabled="true"
              android:exported="true">
      <intent-filter>
        <!-- 例如:接收自定義的廣播 -->
        <action android:name="com.jeanboy.broadcast.MyReceiverFilter" />
      </intent-filter>
    </receiver>
  </application>
</manifest>
複製程式碼

然後在我們傳送廣播時,可以為它指定一個許可權,只有具有該許可權的應用才能接收到廣播,如下所示:

// 建立 Intent
Intent intent = new Intent();
// 例如:新增自定義的 action
intent.setAction(MyReceiver.ACTION);
// 傳送廣播,新增許可權
sendBroadcast(intent, "com.jeanboy.permissions.MY_BROADCAST");
複製程式碼

本地廣播

上面介紹的 BroadcastReceiver 用於應用之間的傳遞訊息,本質上它是跨程式的,還有可能被其他應用攔截。

而 LocalBroadcast(本地廣播)用於應用內部傳遞訊息,比 BroadcastReceiver 更加高效,它只在應用內部有效,不需要考慮安全問題。

本地廣播的建立仍然是繼承 BroadcastReceiver 建立子類,並實現父類的 onReceive() 方法。在註冊、傳送、登出廣播時使用 LocalBroadcastManager 來進行相關操作。

// 建立廣播
MyReceiver myReceiver = new MyReceiver();
// 建立 IntentFilter
IntentFilter intentFilter = new IntentFilter();
// 例如:新增自定義的 action
intentFilter.addAction(MyReceiver.ACTION);
// 註冊本地廣播
LocalBroadcastManager.getInstance(this)
                .registerReceiver(myReceiver, intentFilter);

// 傳送廣播
Intent intent = new Intent(MyReceiver.ACTION));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

// 登出本地廣播
LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
複製程式碼

參考資料

我的 Github

github.com/jeanboydev/…

我的公眾號

歡迎關注我的公眾號,分享各種技術乾貨,各種學習資料,職業發展和行業動態。

Android 波斯灣

技術交流群

歡迎加入技術交流群,來一起交流學習。

QQ 技術交流群

QQ 技術交流群

相關文章