Android的有序廣播和無序廣播(解決安卓8.0版本之后有序廣播的接收問題)


前言

Google從Android8.0版本開始,對在清單文件中靜態注冊廣播做了限制。


特殊廣播(動態注冊廣播接收者)

說:有序廣播和無序廣播之前,咱們先來說下Android中一些特殊的廣播如何接收呢?

  • 特殊的廣播:指那些操作比較頻繁的廣播事件類型。如:屏幕的開、關廣播,電量的變化廣播等等
  • 這種特殊的廣播事件在 AndroidManifest.xml 中注冊是無效的
    因為這種特殊的廣播如果在清單文件中注冊,會浪費內存資源。你可以想象下,如果有100個應用在清單文件中注冊了手機電量變化廣播接收者,那當手機電量發生變化時,這100個應用的廣播接收者就有可能都運行...那會造成什么結果...。所以:只能動態注冊(在代碼中注冊)

如何動態注冊廣播呢

  • 定義一個廣播接收者(兩種方式)
//方式一:動態注冊一個廣播接受者
        BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                    Log.e("動態注冊廣播接收者", "屏幕 關閉 了");
                } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
                    Log.e("動態注冊廣播接收者", "屏幕 開啟 了");
                }
            }
        };

//方式二:單獨定義一個 廣播接收者
public class TestReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //獲取發送的廣播
        String body = intent.getStringExtra("body");
        Log.e("廣播接收者", "body:" + body);
    }
}
  • 動態注冊廣播接收者(注冊定義好的廣播接收者)
//注冊:接收系統廣播 的 廣播接收者
        //創建一個意圖過濾器
        IntentFilter filter = new IntentFilter();
        //為意圖過濾器添加廣播事件類型
        filter.addAction(Intent.ACTION_SCREEN_OFF);//關屏廣播
        filter.addAction(Intent.ACTION_SCREEN_ON);//開屏廣播
        //注冊廣播
        this.registerReceiver(receiver, filter);

// 注冊:自定義的廣播接收者(action的添加有以下兩種方式)
        //方式一:構造器傳參
        IntentFilter filter = new IntentFilter("wo-shi-yi-ge-action");
        //方式二:使用addAction()方法添加
//        IntentFilter filter = new IntentFilter();
//        filter.addAction("wo.ye.shi.yi.ge.action");
        //filter.addCategory("");
        //filter.addDataType("");
        //filter.addDataScheme("");
        this.registerReceiver(receiver2, filter);
  • 發送一個廣播(切記:廣播發送之前要先注冊廣播接收者,否則接收者是接收不到消息的)。針對於自定義的廣播事件。
    切記發送和接收的 action 要保持一致,不然是收不到消息的
        //注冊成功之后,發送一個廣播
        Intent intent = new Intent();
        intent.setAction("wo-shi-yi-ge-action");
        //intent.setAction("wo.ye.shi.yi.ge.action");
        intent.putExtra("body", "我是數據");
        this.sendBroadcast(intent);

無序廣播

  • 類似於新聞聯播,不關心是否有人接收,都會發送。
  • 不可以中止
  • 特殊廣播:也是一種無序廣播。
  • 廣播接收者的兩種注冊方式:靜態注冊 和 動態注冊。動態注冊上面已經敘述過了。下面咱們說說:靜態注冊(分:安卓8.0之前和安卓8.0之后)
  • 先在清單文件(AndroidManifest.xml)中,靜態注冊
        <!--在配置文件中:靜態注冊一個廣播接收者-->
        <receiver android:name=".receiver.TestDemoReceiver">
            <!--定義一個意圖過濾器來接收(監聽)指定的action-->
            <intent-filter>
                <!--可以配置系統的固定的時間類型。如:開關機廣播、撥打電話廣播等等-->
                <!--<action android:name="android.intent.action.ACTION_CLOSE_SYSTEM_DIALOGS" />-->
                <!--也可以配置自定義的 action(事件類型)-->
                <action android:name="x.xx.xxx.無序" />
            </intent-filter>
        </receiver>
  • 發送一個無序廣播(Android版本8.0之前 )
    Intent intent = new Intent();//獲取 Intent 對象
    //使用隱式意圖,為intent添加指定的廣播事件類型
    intent.setAction("x.xx.xxx.無序");
    intent.putExtra("body", "我是數據");//封裝數據
    this.sendBroadcast(intent);//發送廣播
  • 發送一個無序廣播(Android版本8.0之后 ,必須使用全類名方式)
        Intent intent = new Intent();
        intent.setAction("x.xx.xxx.無序");
        //該方式適用:給其他應用的廣播接收者發送消息(指定應用的包名、指定類的全類名)
        //intent.setComponent(new ComponentName("包名", "包名.receiver.TestReceiver"));
        //intent.setClassName("包名", "包名.receiver.TestReceiver");
        //如果是給自身應用內廣播接收者發送廣播
        //intent.setComponent(new ComponentName(this, TestReceiver.class));
        intent.setClassName(this, "包名.receiver.TestReceiver");
        intent.putExtra("body", "我是數據");
        this.sendBroadcast(intent);

有序廣播

  • 類似於中央下發的紅頭文件,一級一級向下發送,中間如果接收失敗,發送就會終止。
  • 發送過程中,可以終止。終止后,后續的接收者則無法接收(有一種特殊情況)。

定義 4 個 廣播接收者:模擬有序廣播的接收

//廣播接收者:有序廣播-1
public class Test1Receiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String resultData = getResultData();
        //有序廣播里終止廣播
        //abortBroadcast();
        setResultData("國家發放補貼800");
        Toast.makeText(context, "省接收:"+resultData, Toast.LENGTH_SHORT).show();
    }
}

//廣播接收者:有序廣播-2
public class Test2Receiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String resultData = getResultData();
        setResultData("國家發放補貼600");
        Toast.makeText(context, "市接收:"+resultData, Toast.LENGTH_SHORT).show();
    }
}

//廣播接收者:有序廣播-3
public class Test3Receiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String resultData = getResultData();
        Toast.makeText(context, "縣接收:"+resultData, Toast.LENGTH_SHORT).show();
    }
}

特殊的廣播接收者:廣播接收者(看成:監察者,用於模擬上述說的有序廣播即便被中斷,依然可以接收到廣播消息的特例)

//廣播接收者:有序廣播 最終接收者
public class Test0Receiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String resultData = getResultData();
        Toast.makeText(context, "最終數據:"+resultData, Toast.LENGTH_SHORT).show();
    }
}
  • 有序廣播接收

Android版本8.0之前(不包含android8.0版本),只需要在清單文件中靜態配置就可以。

  • 在配置文件中(AndroidManifest.xml),如果沒有設置優先級(android:priority="XXX"),那么接收到廣播的先后順序會根據配置文件中書寫的順序產生變化,接收順序會由上而下。這種接收順序不可取,所以,為了避免這種情況,有序廣播的接收者必須配置優先級,防止接收順序錯亂。
  • priority 的優先級:最高1000 —— 最低 -1000

靜態注冊代碼如下:

        <receiver android:name=".receiver.Test1Receiver">
            <intent-filter android:priority="1000">
                <action android:name="x.xx.xxx.有序" />
            </intent-filter>
        </receiver>

        <receiver android:name=".receiver.Test2Receiver">
            <intent-filter android:priority="999">
                <action android:name="x.xx.xxx.有序" />
            </intent-filter>
        </receiver>

        <receiver android:name=".receiver.Test3Receiver">
            <intent-filter android:priority="998">
                <action android:name="x.xx.xxx.有序" />
            </intent-filter>
        </receiver>

從Android版本8.0開始,由於Google對清單文件中靜態注冊廣播接收者做了限制,只能通過動態注冊的方式,實現有序廣播。

動態注冊代碼如下:

        IntentFilter filter2 = new IntentFilter();
        filter2.addAction("x.xx.xxx.有序");
        filter2.setPriority(999);
        this.registerReceiver(new Test2Receiver(), filter2);

        IntentFilter filter1 = new IntentFilter();
        filter1.addAction("x.xx.xxx.有序");
        filter1.setPriority(1000);
        this.registerReceiver(new Test1Receiver(), filter1);

        IntentFilter filter3 = new IntentFilter();
        filter3.addAction("x.xx.xxx.有序");
        filter3.setPriority(998);
        this.registerReceiver(new Test3Receiver(), filter3);

發送一個有序廣播

Test0Receiver:是定義的一個特殊的廣播接收者,在發送有序廣播的時候以參數的形式傳遞進去

  • 若在有序廣播傳遞過程中,假如:A 中止了廣播的傳遞。Test0Receiver 依然會接收到數據。接收到的是: A 發送的數據(如果 A 沒有發送數據,那么Test0Receiver接收到的數據就和 A 接收到的數據相同)。
  • 若在有序廣播傳遞過程中,假如:廣播的傳遞沒有被中止, C作為最后一個接收對象。如果 C沒有繼續發送數據,那么Test0Receiver 接收到的數據 和 C 接收到的數據一樣。如果C有繼續發送數據,那么Test0Receiver 接收到的數據就是C發送的數據
        Intent intent = new Intent();
        intent.setAction("x.xx.xxx.有序");
        //發送一個有序廣播
        //sendOrderedBroadcast(Intent intent,  //發送廣播的意圖對象(可以攜帶數據)
        //          String receiverPermission, //接收權限(如果為空,則不需要權限)
        //          BroadcastReceiver resultReceiver, //廣播接收者對象(自己創建的最終的廣播接收者,可以無須在清單文件中配置,也會接收到廣播 )
        //          Handler scheduler,         //若傳null,則默認是在主線程中
        //          int initialCode,           //初始化的一個值。可默認:Activity.RESULT_OK
        //          String initialData,        //可發送的初始化數據(相當於一條廣播數據)。可為null
        //          Bundle initialExtras)      //可綁定數據傳遞(Intent對象也可以,所以可為null)
        this.sendOrderedBroadcast(intent, null, new Test0Receiver(), null, Activity.RESULT_OK, "國家發放補貼1000", null);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM