Android四大控件之一:BroadcastReceiver詳解

目錄

01.什麼是廣播Broadcast

02.廣播Broadcast作用

03.廣播Broadcast分類

04.靜態註冊BroadCast

05.動態註冊BroadCast

06.發送本地廣播

07.使用私有權限

08.系統廣播



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,用於個人複習總結和分享,若有不足之處,歡迎指正。

 

 

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