Android開發系列5——BroadcastReceiver詳解

前言

BroadcastReceiver(廣播接收器)是Android四大組價之一,是一種廣泛運用的在應用之間消息傳輸機制。顧名思義,是一個接收廣播(broadcast intent)的一個類。所以想理解好BroadcastReceiver工作原理,必須帶着一些問題去看:

  • 1.廣播的機制是什麼?
  • 2.廣播是怎麼發送出來的?
  • 3.廣播內容是什麼,怎麼傳遞給接收器?
  • 4.BroadcastReceiver怎麼接收廣播的內容?
  • 5.廣播的種類?

Android的廣播機制是一個典型的:發佈-訂閱模式,就是觀察者模式。(很多博客會把廣播機制說成:觀察者模式來說)。它最大的特點就是發送方不關心接收方是否接收到數據,也不關心接收方是如何處理數據。通過這樣的形式來達到接收雙方的完全解耦。一個廣播可以有一個或多個接收者。

監聽器模式: 事件源經過事件的封裝傳給監聽器,當事件源觸發事件後,監聽器接收到事件對象可以回調事件的方法


觀察者模式: 觀察者(Observer)相當於事件監聽者,被觀察者(Observable)相當於事件源和事件,執行邏輯時通知observer即可觸發oberver的update, 同時可傳參數給被觀察者

廣播的類型

  • 普通廣播(Normal Broadcast)
  • 有序廣播(Ordered Broadcast)
  • 本地廣播(Local Broadcast)
  • 粘性廣播(Sticky Broadcast)
  • 系統廣播(System Broadcast)

BroadcastReceiver 註冊類型

  • 靜態註冊(常駐)
  • 動態註冊(非常駐)

一、普通廣播(Normal Broadcast)

普通廣播(Normal Broadcast)是一種異步、無序。

  • 優點: 傳播效率高
  • 缺點: BroadcastReceiver 接收無序,每個接收器之間不能把處理的結果傳給下一個接收者。不能終止已經發出的廣播。

Activity 頁面:

public class MainActivity extends AppCompatActivity {
    private final String NORMAL_ACTION = "com.ftimage.NormalBroadcast";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(NORMAL_ACTION);
                intent.putExtra("Broad_Msg", "Hello World!Broad :");
				
				// 廣播中攜帶數據
                Bundle bundle = new Bundle();
                bundle.putString("Broad_Local","Send Broad before!");

                sendBroadcast(intent);
            }
        });
    }
}



BroadcastReceiver A

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class NormalOneReceiver extends BroadcastReceiver {

    private static final String tag = "NormalOneReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        
        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);

    }
}

AndroidManifest.XML 文件配置:

<receiver android:name=".NormalOneReceiver">
        <intent-filter>
            <action android:name="com.ftimage.NormalBroadcast"></action>
         </intent-filter>
</receiver>

二、有序廣播(Ordered Broadcast)

有序廣播(Ordered Broadcast)是同步執行的廣播。廣播發出去同一時刻只有一個廣播接收器能收到這條消息,當本條接收器執行完畢,廣播才繼續傳播。

  • 優點: 廣播可以根據優先級先後執行(AndroidManifest.xml中receiver標籤下intent-filter中的android:priority屬性來設置,數值越大優先級越高,數值越小優先級越低);可以中斷廣播(abortBroadcast()方法);接收者之間可以使用上一個接收者處理後的結果。
  • 缺點: 效率比較低。

Activity 頁面:

public class MainActivity extends AppCompatActivity {

   private final String NORMAL_ACTION = "com.ftimage.OrderBroadcast";
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Intent intent = new Intent(NORMAL_ACTION);
               intent.putExtra("Broad_Msg", "Hello World!Broad :");

               Bundle bundle = new Bundle();
               bundle.putString("Broad_Local","Send Broad before!");
   			   sendOrderedBroadcast(intent,null);

           }
       });

   }
}

BroadcastReceiver的類:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;


// BroadcastReceiver 第一個
public class OrderOneReceiver extends BroadcastReceiver {
    private static final String tag = "OrderOneReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {

        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);

        Bundle bundle = new Bundle();
        bundle.putString("msg", "OrderOneReceiver的結果傳遞給下一個Receiver");
        setResultExtras(bundle);
    }
}


// BroadcastReceiver 第二個
public class OrderTwoReceiver extends BroadcastReceiver {
    private static final String tag = "OrderTwoReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {

        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);

        String resultMsg = getResultExtras(true).getString("msg");
        Log.e(tag, "從上一條Receiver獲取的結果:" + resultMsg);


        Bundle bundle = new Bundle();
        bundle.putString("msg", "OrderTwoReceiver的結果傳遞給下一個Receiver");
        setResultExtras(bundle);

    }
}


// BroadcastReceiver 第三個
public class OrderThirdReceiver extends BroadcastReceiver {

    private static final String tag = "OrderThirdReceiver";
    private Intent receverIntent;
    @Override
    public void onReceive(Context context, Intent intent) {

        String msg = intent.getStringExtra("Broad_Msg");
        Log.e(tag, msg + tag);


        String resultMsg = getResultExtras(true).getString("msg");
        Log.e(tag, "從上一條Receiver獲取的結果:" + resultMsg);

    }
}

其中,setResultExtras() 和 getResultExtras()是BroadcastReceiver消息傳遞的類方法。

AndroidManifest.XML 文件配置:

<receiver android:name=".OrderOneReceiver">
            <intent-filter android:priority="110">
                <action android:name="com.ftimage.OrderBroadcast"></action>
            </intent-filter>
        </receiver>

        <receiver android:name=".OrderTwoReceiver">
            <intent-filter android:priority="100">
                <action android:name="com.ftimage.OrderBroadcast"></action>
            </intent-filter>
        </receiver>

        <receiver android:name=".OrderThirdReceiver">
            <intent-filter android:priority="90">
                <action android:name="com.ftimage.OrderBroadcast"></action>
            </intent-filter>
        </receiver>

打印出來的log:

03-18 17:02:52.976 4520-4520/com.ftimage.firstandroid E/OrderOneReceiver: Hello World!Broad :OrderOneReceiver
03-18 17:02:52.980 4520-4520/com.ftimage.firstandroid E/OrderTwoReceiver: Hello World!Broad :OrderTwoReceiver
03-18 17:02:52.980 4520-4520/com.ftimage.firstandroid E/OrderTwoReceiver: 從上一條Receiver獲取的結果:OrderOneReceiver的結果傳遞給下一個Receiver
03-18 17:02:52.984 4520-4520/com.ftimage.firstandroid E/OrderThirdReceiver: Hello World!Broad :OrderThirdReceiver
03-18 17:02:52.984 4520-4520/com.ftimage.firstandroid E/OrderThirdReceiver: 從上一條Receiver獲取的結果:OrderTwoReceiver的結果傳遞給下一個Receiver

可以看出 Receiver 接收廣播時,“priority”越大優先級越高,且 Receiver之間也能通過接收器的類方法傳遞數據

此外,BroadcastReceiver 也能調用 abortBroadcast() 方法截斷廣播,會使低優先級的廣播接收器就無法接收到廣播

三、本地廣播(Local Broadcast)

本地廣播只能在應用內部進行傳遞,而且廣播接收器也只能接收本應用內自身發出的廣播

本地廣播是使用 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";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "----本地廣播-----");
    } 
}

Activity上的

public class MainActivity extends AppCompatActivity  {

    private LocalBroadcastManager localBroadcastManager;
    private LocalReceiver localReceiver;

    private final String LOCAL_ACTION = "com.ftimage.local";

    @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);

        findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent(LOCAL_ACTION);
                localBroadcastManager.sendBroadcast(intent);
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 銷燬本地監聽
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
}

四、粘性廣播(Sticky Broadcast)

粘性廣播通過Context.sendStickBroadcast()方法來發送,用此方法發送的廣播會一直滯留,當有匹配此廣播的接收器被註冊後,該廣播接收器就會收到此廣播。使用此廣播時,需要獲得BROADCAST_STICKY權限。(在 android 5.0/api 21後不再推薦使用)

<uses-permission android:name="android.permission.BROADCAST_STICKY" />

五、系統廣播(System Broadcast)

Android系統中內置了多個系統廣播,只要涉及到手機的基本操作,基本上都會發出相應的系統廣播。如:開啓啓動,網絡狀態改變,拍照,屏幕關閉與開啓,點亮不足等等。每個系統廣播都具有特定的intent-filter,其中主要包括具體的action,系統廣播發出後,將被相應的BroadcastReceiver接收。系統廣播在系統內部當特定事件發生時,有系統自動發出,就不一一列舉了,可以根據自己需要在進行查找。

六、靜態註冊和動態註冊

靜態註冊:

就是將廣播的註冊行爲在清單文件AndroidManifest.xml中進行。靜態註冊的廣播接收者只要App在系統中運行則一直可以接收到廣播消息,上邊的例子都是靜態註冊。

application標籤內出現了一個新的標籤receiver,所有靜態註冊的廣播接收器都是在這裏進行註冊的。通過android:name來指定具體註冊哪一個廣播接收器。然後在intent-filter標籤里加入想要接收的廣播。 要銷燬靜態註冊的廣播接收器可以通過PackageManager的Receiver禁用。

動態註冊:

直接使用IntentFilter來註冊,例子如下:


public class MainActivity extends AppCompatActivity {

   private final String NORMAL_ACTION = "com.ftimage.DynamicReceiver";
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
		
		IntentFilter filter = new IntentFilter();
        filter.addAction(NORMAL_ACTION);
        dynamicReceiver = new DynamicReceiver();	          			
        registerReceiver(dynamicReceiver,filter);

       findViewById(R.id.sendBroad).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Intent intent = new Intent();
         //設置與動態相同的Action,方便同時觸發靜態與動態
         intent.setAction(NORMAL_ACTION);
         intent.putExtra("info","您現在使用的是動態註冊");
         sendBroadcast(intent);//默認廣播
           }
       });
   }

	class DynamicReceiver extends BroadcastReceiver{
         @Override
         public void onReceive(Context context, Intent intent) {
            Toast t = Toast.makeText(context,"動態廣播:"+ intent.getStringExtra("info"), Toast.LENGTH_SHORT);
             t.setGravity(Gravity.TOP,0,0);
             t.show();
         }
     }
}

七、BroadcastCeceiver使用的細節

發送和接收到的廣播全都是屬於系統全局廣播,即發出的廣播可以被其他應用接收到,而且也可以接收到其他應用發送出的廣播,這樣可能會有不安全因素。使用動態註冊廣播接收器存在一個問題,即系統內的任何應用均可監聽並觸發我們的 Receiver 。可以通過在AndroidManifest.XML文件中的< receiver > 標籤添加一個android:exported=“false” 屬性,標明該 Receiver 僅限應用內部使用。這樣,系統中的其他應用就無法接觸到該 Receiver 了

此外,也可以選擇創建自己的使用權限,即在清單文件中添加一個 < permission > 標籤來聲明自定義權限

<permission
        android:name="com.example.permission.receiver"
        android:protectionLevel="signature" />

自定義權限時必須同時指定 protectionLevel 屬性值,系統根據該屬性值確定自定義權限的使用方式

屬性值 限定方式
normal 默認值。較低風險的權限,對其他應用,系統和用戶來說風險最小。系統在安裝應用時會自動批准授予應用該類型的權限,不要求用戶明確批准(雖然用戶在安裝之前總是可以選擇查看這些權限)
dangerous 較高風險的權限,請求該類型權限的應用程序會訪問用戶私有數據或對設備進行控制,從而可能對用戶造成負面影響。因爲這種類型的許可引入了潛在風險,所以系統可能不會自動將其授予請求的應用。例如,系統可以向用戶顯示由應用請求的任何危險許可,並且在繼續之前需要確認,或者可以採取一些其他方法來避免用戶自動允許
signature 只有在請求該權限的應用與聲明權限的應用使用相同的證書籤名時,系統纔會授予權限。如果證書匹配,系統會自動授予權限而不通知用戶或要求用戶的明確批准
signatureOrSystem 系統僅授予Android系統映像中與聲明權限的應用使用相同的證書籤名的應用。請避免使用此選項,“signature”級別足以滿足大多數需求,“signatureOrSystem”權限用於某些特殊情況

總結

廣播(Broadcast)是在組件之間傳播數據的一種機制,這些組件可以位於不同的進程中,起到進程間通信的作用。但是廣播存在一個非常大的效率問題,所以在使用過程中需要注意使用。

持續更新中……

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