Android 四大元件之 BroadcastReceiver

Keven發表於2019-04-07

讀前思考

學習一門技術或者看一篇文章最好的方式就是帶著問題去學習,這樣才能在過程中有茅塞頓開、燈火闌珊的感覺,記憶也會更深刻。

  1. 廣播有幾種形式?什麼特點?
  2. 廣播的兩種註冊形式?區別在哪?
  3. 對於 8.0+ 系統廣播會有何不同?

兩種註冊方式

廣播的註冊有兩種方法:
一種在活動裡通過程式碼動態註冊,另一種在配置檔案裡靜態註冊
兩種方式的相同點是都完成了對接收器以及它能接收的廣播值這兩個值的定義;不同點是動態註冊的接收器必須要在程式啟動之後才能接收到廣播,而靜態註冊的接收器即便程式未啟動也能接收到廣播,比如想接收到手機開機完成後系統發出的廣播就只能用靜態註冊了。

廣播的四種形式

普通廣播、有序廣播、本地廣播、粘性廣播

1. 普通廣播

普通廣播是一種完全非同步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播訊息,因此它們接收的先後是隨機的。另外,接收器不能截斷普通廣播。

廣播傳送(需要適配 8.0+)

安卓系統 8.0 對廣播做了如下修改:除了有限的例外情況,應用無法使用清單註冊隱式廣播。 它們仍然可以在執行時註冊這些廣播,並且可以使用清單註冊專門針對它們的顯式廣播。

//如果廣播接收器為動態註冊或 8.0 以下系統的靜態註冊,則使用如下進行傳送

//設定 action
Intent intent = new Intent("com.keven.receiver.MY_BROCASTRECEIVER");
//設定傳遞資料
Bundle bundle=new Bundle();
bundle.putString("key","Hello Receiver!");
intent.putExtras(bundle);
//傳送廣播
sendBroadcast(intent);
複製程式碼
//如果在 8.0+ 廣播接收器仍想用靜態註冊,則使用如下進行傳送


Intent intent = new Intent("com.keven.receiver.MY_BROCASTRECEIVER");
//第一個引數為廣播接收器所在應用的包名
//第二個引數為廣播接收器包名+類名
intent.setComponent(new ComponentName("com.keven.jianshu","com.keven.jianshu.part1.MyReceiver"));
Bundle bundle=new Bundle();
bundle.putString("key","Hello Receiver!");
intent.putExtras(bundle);
sendBroadcast(intent);
複製程式碼

==注意:== 如果動態註冊的廣播接收器,當在傳送廣播時使用 setComponent( ) , 反而是收不到廣播的。

廣播接收

  1. 新建類繼承 BroadcastReceiver
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        String value = extras.getString("key");
        LogUtils.i("MyReceiver 收到廣播,廣播內容是:"+value);
    }
}
複製程式碼
  1. 在清單檔案中(或動態註冊)廣播接收器
<!-- 清單檔案中註冊廣播接收器 -->
<receiver
        android:name=".part1.MyReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.keven.receiver.MY_BROCASTRECEIVER" />
        </intent-filter>
</receiver>
複製程式碼
//程式碼中動態註冊廣播接收器
IntentFilter filter = new IntentFilter();
filter.addAction("com.keven.receiver.MY_BROCASTRECEIVER");
MyReceiver receiver = new MyReceiver();
registerReceiver(receiver, filter);

/**
*在 onDestory( ) 方法中解綁
*/
unregisterReceiver(receiver);
複製程式碼

當正確註冊廣播接收器併傳送廣播後,廣播接收器可以正常收到廣播。

com.keven.jianshu I/TAG: MyReceiver 收到廣播,廣播內容是:Hello Receiver!
複製程式碼

2. 有序廣播

有序廣播是一種同步執行的廣播,在廣播發出之後,同一時刻只會有一個廣播接收器能夠收到這條廣播訊息,當這個廣播接收器中的邏輯執行完畢後,廣播才會繼續傳遞,所以此時的廣播接收器是有先後順序的,且優先順序(priority)高的廣播接收器會先收到廣播訊息。有序廣播可以被接收器截斷使得後面的接收器無法收到它。

廣播傳送

Intent intent = new Intent("com.keven.receiver.MY_BROCASTRECEIVER");
Bundle bundle=new Bundle();
bundle.putString("key","Hello Receiver!");
intent.putExtras(bundle);
sendOrderedBroadcast(intent,null);
複製程式碼

廣播接收

//第一個廣播接收器
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        String value = extras.getString("key");
        LogUtils.i("MyReceiver 收到廣播,廣播內容是:"+value);
    }
}


//第二個廣播接收器
public class MyReceiver2 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        String value = extras.getString("key");
        LogUtils.i("MyReceiver2 收到廣播,廣播內容是:"+value);
    }
}
複製程式碼

廣播註冊(為了方便適配直接使用動態註冊)

filter1 = new IntentFilter();
filter1.addAction("com.keven.receiver.MY_BROCASTRECEIVER");
filter1.setPriority(100);
receiver = new MyReceiver();
registerReceiver(receiver, filter1);

filter2 = new IntentFilter();
filter2.addAction("com.keven.receiver.MY_BROCASTRECEIVER");
filter2.setPriority(200);
receiver2 = new MyReceiver2();
registerReceiver(receiver2, filter2);


/**
*在 onDestory( ) 方法中解綁
*/
unregisterReceiver(receiver);
unregisterReceiver(receiver2);
複製程式碼

結果輸出

com.keven.jianshu I/TAG: MyReceiver2 收到廣播,廣播內容是:Hello Receiver!
com.keven.jianshu I/TAG: MyReceiver 收到廣播,廣播內容是:Hello Receiver!
複製程式碼

可以看到,廣播接收器 2 先收到了廣播,接收器 1 後收到了廣播,所以 Priority 數值越大,優先順序越高,越先收到廣播。

如果在廣播接收器中呼叫 abortBroadcast( ) 方法,則之後的接收器無法收到該廣播!

public class MyReceiver2 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        String value = extras.getString("key");
        LogUtils.i("MyReceiver2 收到廣播,廣播內容是:"+value);
        
        //如果在廣播接收器中呼叫 abortBroadcast( ) 方法,則之後的接收器無法收到該廣播
        abortBroadcast();
    }
}
複製程式碼

3. 本地廣播

本地廣播機制,使用這個機制發出的廣播只能夠在應用程式的內部進行傳遞,並且廣播接收器也只能接收本應用程式發出的廣播。

傳送廣播

//先獲取 LocalBroadcastManager
LocalBroadcastManager localBroadcastManager=LocalBroadcastManager.getInstance(this);
Intent intent = new Intent("com.keven.receiver.MY_BROCASTRECEIVER");
Bundle bundle=new Bundle();
bundle.putString("key","Hello Receiver!");
intent.putExtras(bundle);
//通過 LocalBroadcastManager 進行廣播傳送
localBroadcastManager.sendBroadcast(intent);
複製程式碼

廣播接收

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();
        String value = extras.getString("key");
        LogUtils.i("MyReceiver 收到廣播,廣播內容是:"+value);
    }
}
複製程式碼

廣播註冊

//先獲取 LocalBroadcastManager
localBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);
filter1 = new IntentFilter();
filter1.addAction("com.keven.receiver.MY_BROCASTRECEIVER");
filter1.setPriority(100);
receiver = new MyReceiver();
//通過 LocalBroadcastManager 進行廣播註冊
localBroadcastManager.registerReceiver(receiver, filter1);


/**
*在 onDestory( ) 方法中使用 LocalBroadcastManager 解綁
*/
localBroadcastManager.unregisterReceiver(receiver);
複製程式碼

4. 粘性廣播

通過 Context.sendStickyBroadcast() 方法可傳送粘性 (sticky) 廣播,這種廣播會一直滯留,當有匹配該廣播的接收器被註冊後,該接收器就會收到此條廣播。

注意,傳送粘性廣播還需要BROADCAST_STICKY許可權:

<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
複製程式碼

sendStickyBroadcast() 只保留最後一條廣播,並且一直保留下去,這樣即使已經有廣播接收器處理了該廣播,一旦又有匹配的廣播接收器被註冊,該粘性廣播仍會被接收。如果只想處理一遍該廣播,可通過 removeStickyBroadcast() 方法來實現。接收粘性廣播的過程和普通廣播是一樣的,不再過多贅述。

文章已經讀到末尾了,不知道最初的幾個問題你都會了嗎?如果不會的話?可以再針對不會的問題進行精讀哦!答案都在文中,相信你肯定可以解決的!

相關文章