目錄
01.什麼是廣播Broadcast
- 在 Android 系統中,廣播(Broadcast)是在組件之間傳播數據的一種機制,這些組件可以位於不同的進程中,起到進程間通信的作用。
- BroadcastReceiver 是對發送出來的Broadcast進行過濾、接受和響應的組件。首先將要發送的消息和用於過濾的信息(Action,Category)裝入一個 Intent對象,然後通過調用Context.sendBroadcast()、sendOrderBroadcast() 方法把 Intent 對象以廣播形式發送出去。 廣播發送出去後,所以已註冊的 BroadcastReceiver 會檢查註冊時的 IntentFilter 是否與發送的 Intent 相匹配,若匹配則會調用 BroadcastReceiver 的 onReceiver() 方法
- 所以當我們定義一個 BroadcastReceiver 的時候,都需要實現 onReceiver() 方法。BroadcastReceiver 的生命週期很短,在執行 onReceiver() 方法時纔有效,一旦執行完畢,該Receiver 的生命週期就結束了。
-
BroadcastReceiver的生命週期,從對象調用它開始,到onReceiver方法執行完成之後結束。另外,每次廣播被接收後會重新創建BroadcastReceiver對象,並在onReceiver方法中執行完就銷燬,如果BroadcastReceiver的onReceiver方法中不能在10秒內執行完成,Android會出現ANR異常。所以不要在BroadcastReceiver的onReceiver方法中執行耗時的操作.
-
如果需要在BroadcastReceiver中執行耗時的操作,可以通過Intent啓動Service來完成。但不能綁定Service。如果我們在Activity中註冊了BroadcastReceiver,當這個Activity銷燬的時候要主動撤銷註冊否則會出現異常
02.廣播Broadcast作用
- 用於監聽 / 接收 應用發出的廣播消息,並做出響應
- 應用場景
- a.不同組件之間通信(包括應用內 / 不同應用之間)
- b.與
Android
系統在特定情況下的通信:如當電話呼入時、網絡可用時- c.多線程通信
03.廣播Broadcast分類
- Android中的廣播分爲兩種類型,標準廣播和有序廣播
- 標準廣播
- 標準廣播是一種完全異步執行的廣播,在廣播發出後所有的廣播接收器會在同一時間接收到這條廣播,之間沒有先後順序,效率比較高,且無法被截斷。
- 有序廣播
- 有序廣播是一種同步執行的廣播,在廣播發出後同一時刻只有一個廣播接收器能夠接收到, 優先級高的廣播接收器會優先接收,當優先級高的廣播接收器的 onReceiver() 方法運行結束後,廣播纔會繼續傳遞,且前面的廣播接收器可以選擇截斷廣播,這樣後面的廣播接收器就無法接收到這條廣播了。
- 標準廣播
04.靜態註冊BroadCast
- 靜態註冊在清單文件配置
- 即在清單文件中爲 BroadcastReceiver 進行註冊,使用**< receiver >**標籤聲明,並在標籤內用 < intent-filter > 標籤設置過濾器。這種形式的BroadcastReceiver的生命週期伴隨着整個應用,如果這種方式處理的是系統廣播,那麼不管應用是否在運行,該廣播接收器都能接收到該廣播。
- 代碼如下所示
- 首先,繼承 BroadcastReceiver 類創建一個用於接收標準廣播的Receiver,在 onReceive() 方法中取出 Intent 傳遞來的字符串
public class NormalReceiver extends BroadcastReceiver { public NormalReceiver() { } @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("Msg"); LogUtils.i("廣播NormalReceiver------"+msg); } }
4.1 發送標準廣播
- 在清單文件中聲明的 BroadcastReceiver ,必須包含值爲 NORMAL_ACTION 字符串的 action 屬性,該廣播接收器才能收到以下代碼中發出的廣播
- 發送標準廣播調用的是 sendBroadcast(Intent) 方法
public class MainActivity extends AppCompatActivity { private final String NORMAL_ACTION = "com.example.normal.receiver"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void sendBroadcast(View view) { Intent intent = new Intent(NORMAL_ACTION); intent.putExtra("Msg", "逗比"); sendBroadcast(intent); } }
- 在清單文件中註冊 BroadcastReceiver
<application> <receiver android:name=".NormalReceiver"> <intent-filter> <action android:name="com.example.normal.receiver" /> </intent-filter> </receiver> </application>
4.2 發送有序廣播
- 首先,繼承 BroadcastReceiver 類創建三個用於接收有序廣播的Receiver,名字依次命名爲 OrderReceiver_1、OrderReceiver_2、OrderReceiver_3。
- 此外,既然 Receiver 在接收廣播時存在先後順序,那麼 Receiver 除了能從發送廣播使用的 Intent 接收數據外,優先級高的 Receiver 也能在處理完操作後向優先級低的 Receiver 傳送處理結果。
public class OrderReceiver_1 extends BroadcastReceiver { private final String TAG = "OrderReceiver_1"; public OrderReceiver_1() { } @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "OrderReceiver_1被調用了"); //取出Intent當中傳遞來的數據 String msg = intent.getStringExtra("Msg"); Log.e(TAG, "OrderReceiver_1接收到的值: " + msg); //向下一優先級的Receiver傳遞數據 Bundle bundle = new Bundle(); bundle.putString("Data", "(Hello)"); setResultExtras(bundle); } } public class OrderReceiver_2 extends BroadcastReceiver { private final String TAG = "OrderReceiver_2"; public OrderReceiver_2() { } @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "OrderReceiver_2被調用了"); //取出上一優先級的Receiver傳遞來的數據 String data = getResultExtras(true).getString("Data"); Log.e(TAG, "從上一優先級的Receiver傳遞來的數據--" + data); //向下一優先級的Receiver傳遞數據 Bundle bundle = new Bundle(); bundle.putString("Data", "(葉應是葉)"); setResultExtras(bundle); } } public class OrderReceiver_3 extends BroadcastReceiver { private final String TAG = "OrderReceiver_3"; public OrderReceiver_3() { } @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "OrderReceiver_3被調用了"); //取出上一優先級的Receiver傳遞來的數據 String data = getResultExtras(true).getString("Data"); Log.e(TAG, "從上一優先級的Receiver傳遞來的數據--" + data); } }
- 在清單文件中對三個 Receiver 進行註冊,指定相同的 action 屬性值,Receiver 之間的優先級使用 priority 屬性來判定,數值越大,優先級越高
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".OrderReceiver_1"> <intent-filter android:priority="100"> <action android:name="com.example.order.receiver" /> </intent-filter> </receiver> <receiver android:name=".OrderReceiver_2"> <intent-filter android:priority="99"> <action android:name="com.example.order.receiver" /> </intent-filter> </receiver> <receiver android:name=".OrderReceiver_3"> <intent-filter android:priority="98"> <action android:name="com.example.order.receiver" /> </intent-filter> </receiver> </application>
- 發送有序廣播調用的是 **sendOrderedBroadcast(Intent,String)**方法,String參數值在自定義權限時使用,下邊會有介紹
public class MainActivity extends AppCompatActivity { private final String NORMAL_ACTION = "com.example.normal.receiver"; private final String ORDER_ACTION = "com.example.order.receiver"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void sendBroadcast(View view) { Intent intent = new Intent(NORMAL_ACTION); intent.putExtra("Msg", "Hi"); sendBroadcast(intent); } public void sendOrderBroadcast(View view) { Intent intent = new Intent(ORDER_ACTION); intent.putExtra("Msg", "Hi"); sendOrderedBroadcast(intent, null); } }
05.動態註冊BroadCast
- 動態註冊 BroadcastReceiver 是在代碼中定義並設置好一個 IntentFilter 對象,然後在需要註冊的地方調用 Context.registerReceiver() 方法,調用 Context.unregisterReceiver() 方法取消註冊,此時就不需要在清單文件中註冊 Receiver 了
- 這裏採用在 Service 中註冊廣播接收器的形式,分別在註冊廣播接收器、取消註冊廣播接受器和接收到廣播時輸出Log。
public class BroadcastService extends Service { private BroadcastReceiver receiver; private final String TAG = "BroadcastService"; public BroadcastService() { } @Override public void onCreate() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(MainActivity.ACTION); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "BroadcastService接收到了廣播"); } }; registerReceiver(receiver, intentFilter); Log.e(TAG, "BroadcastService註冊了接收器"); super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); unregisterReceiver(receiver); Log.e(TAG, "BroadcastService取消註冊接收器"); } @Override public IBinder onBind(Intent intent) { return null; } }
- 提供啓動服務,停止服務、發送廣播的方法
public class MainActivity extends AppCompatActivity { public final static String ACTION = "com.example.receiver"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void startService(View view) { Intent intent = new Intent(this, BroadcastService.class); startService(intent); } public void sendBroadcast(View view) { Intent intent = new Intent(ACTION); sendBroadcast(intent); } public void stopService(View view) { Intent intent = new Intent(this, BroadcastService.class); stopService(intent); } }
- 動態廣播最好在Activity的onResume()註冊、onPause()註銷。
- 原因:對於動態廣播,有註冊就必然得有註銷,否則會導致內存泄露
- 重複註冊、重複註銷也不允許
06.發送本地廣播
- 之前發送和接收到的廣播全都是屬於系統全局廣播,即發出的廣播可以被其他應用接收到,而且也可以接收到其他應用發送出的廣播,這樣可能會有不安全因素
- 因此,在某些情況下可以採用本地廣播機制,使用這個機制發出的廣播只能在應用內部進行傳遞,而且廣播接收器也只能接收本應用內自身發出的廣播
- 本地廣播是使用 LocalBroadcastManager 來對廣播進行管理
函數 作用 LocalBroadcastManager.getInstance(this).registerReceiver(BroadcastReceiver, IntentFilter) 註冊Receiver LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver); 註銷Receiver LocalBroadcastManager.getInstance(this).sendBroadcast(Intent) 發送異步廣播 LocalBroadcastManager.getInstance(this).sendBroadcastSync(Intent) 發送同步廣播 - 首先,創建一個 BroadcastReceiver 用於接收本地廣播
public class LocalReceiver extends BroadcastReceiver { private final String TAG = "LocalReceiver"; public LocalReceiver() { } @Override public void onReceive(Context context, Intent intent) { Log.e(TAG, "接收到了本地廣播"); } }
- 之後就是使用 LocalBroadcastManager 對 LocalReceiver 進行註冊和解除註冊了
private LocalBroadcastManager localBroadcastManager; private LocalReceiver localReceiver; private final String LOCAL_ACTION = "com.example.local.receiver"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager = LocalBroadcastManager.getInstance(this); localReceiver = new LocalReceiver(); IntentFilter filter = new IntentFilter(LOCAL_ACTION); localBroadcastManager.registerReceiver(localReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(batteryReceiver); localBroadcastManager.unregisterReceiver(localReceiver); } public void sendLocalBroadcast(View view) { Intent intent = new Intent(LOCAL_ACTION); localBroadcastManager.sendBroadcast(intent); }
- 需要注意
- 本地廣播是無法通過靜態註冊的方式來接收的,因爲靜態註冊廣播主要是爲了在程序未啓動的情況下也能接收廣播,而本地廣播是應用自己發送的,此時應用肯定是啓動的了
07.使用私有權限
- 使用動態註冊廣播接收器存在一個問題,即系統內的任何應用均可監聽並觸發我們的 Receiver 。通常情況下我們是不希望如此的
- 解決辦法之一是在清單文件中爲 < receiver > 標籤添加一個 android:exported="false" 屬性,標明該 Receiver 僅限應用內部使用。這樣,系統中的其他應用就無法接觸到該 Receiver 了
<permission android:name="com.example.permission.receiver" android:protectionLevel="signature" />
- 此外,也可以選擇創建自己的使用權限,即在清單文件中添加一個 < permission > 標籤來聲明自定義權限
- 自定義權限時必須同時指定 protectionLevel 屬性值,系統根據該屬性值確定自定義權限的使用方式
屬性值 限定方式 normal 默認值。較低風險的權限,對其他應用,系統和用戶來說風險最小。系統在安裝應用時會自動批准授予應用該類型的權限,不要求用戶明確批准(雖然用戶在安裝之前總是可以選擇查看這些權限) dangerous 較高風險的權限,請求該類型權限的應用程序會訪問用戶私有數據或對設備進行控制,從而可能對用戶造成負面影響。因爲這種類型的許可引入了潛在風險,所以系統可能不會自動將其授予請求的應用。例如,系統可以向用戶顯示由應用請求的任何危險許可,並且在繼續之前需要確認,或者可以採取一些其他方法來避免用戶自動允許 signature 只有在請求該權限的應用與聲明權限的應用使用相同的證書籤名時,系統纔會授予權限。如果證書匹配,系統會自動授予權限而不通知用戶或要求用戶的明確批准 signatureOrSystem 系統僅授予Android系統映像中與聲明權限的應用使用相同的證書籤名的應用。請避免使用此選項,“signature”級別足以滿足大多數需求,“signatureOrSystem”權限用於某些特殊情況 - 首先,新建一個新的工程,在它的清單文件中創建一個自定義權限,並聲明該權限。protectionLevel 屬性值設爲“signature”
<permission android:name="com.example.permission.receiver" android:protectionLevel="signature" /> <uses-permission android:name="com.example.permission.receiver" />
- 然後,發送含有該權限聲明的 Broadcast 。這樣,只有使用相同證書籤名且聲明該權限的應用才能接收到該 Broadcast 了
private final String PERMISSION_PRIVATE = "com.example.permission.receiver"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void sendPermissionBroadcast(View view) { sendBroadcast(new Intent("Hi"), PERMISSION_PRIVATE); }
- 回到之前的工程。首先在清單文件中聲明權限
<uses-permission android:name="com.example.permission.receiver" />
創建一個 BroadcastReceiver
public class PermissionReceiver extends BroadcastReceiver {
private final String TAG = "PermissionReceiver";
public PermissionReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "接收到了私有權限廣播");
}
}
- 然後註冊廣播接收器。因爲 AndroidStudio在調試的時候會使用相同的證書爲每個應用簽名,所以,在之前新安裝的App發送出廣播後,PermissionReceiver 就會輸出 Log 日誌
private final String PERMISSION_PRIVATE = "com.example.permission.receiver"; private PermissionReceiver permissionReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); IntentFilter intentFilter1 = new IntentFilter("Hi"); permissionReceiver = new PermissionReceiver(); registerReceiver(permissionReceiver, intentFilter1, PERMISSION_PRIVATE, null); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(permissionReceiver); }
08.系統廣播
- 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 |
注:當使用系統廣播時,只需要在註冊廣播接收者時定義相關的action即可,並不需要手動發送廣播,當系統有相關操作時會自動進行系統廣播
本文參考了好幾篇關於BroadcastReceiver的博客和Demo,用於個人複習總結和分享,若有不足之處,歡迎指正。