簡介
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
我的公眾號
歡迎關注我的公眾號,分享各種技術乾貨,各種學習資料,職業發展和行業動態。
技術交流群
歡迎加入技術交流群,來一起交流學習。