Android——廣播機制原理

一、廣播類型

Andriod 中的廣播主要分爲兩個類型:標準廣播有序廣播

標準廣播(Normal broadcasts)是一種完全異步執行的廣播。在廣播發送後,所有的廣播接收器都可以在同一時刻接收到這條廣播信息,然後各自進行相應的邏輯處理。標準廣播的優點是效率高,但與此同時,也意味着標準廣播是無法被截斷的,所有的廣播接收器都可以接受到。標準廣播示意圖如下:

 

有序廣播(Ordered broadcasts)是一種同步執行的廣播。在廣播發出後,同一時間只能有一個優先級最高的廣播接收器來接收。在優先級高的廣播接收器接收到廣播並且執行完內部邏輯後,廣播纔會繼續往優先級低的廣播接收器傳遞,或者優先級高的廣播接收器判定廣播不需要繼續往下傳遞的時候,則會截斷廣播,這樣後面的廣播接收器就不會接收到廣播了。有序廣播示意圖如下:

 

二、註冊廣播接收器

Android內置了許多系統級別的廣播接收器,如更換系統語言、電池電量變化等都會發出相應的廣播。如果我們希望在自己的應用中可以接收到這些廣播,就需要註冊對應的廣播接收器。

廣播接收器的註冊有兩種方式:動態註冊靜態註冊。動態註冊就是在代碼中註冊,當你需要用到廣播接收器的時候直接在代碼中註冊;而靜態註冊則是在AndroidManifest.xml文件中進行註冊。

1、動態註冊廣播接收器

動態註冊廣播接收器只需要創建一個繼承BroadcastReceiver的類,並且重寫父類onReceiver()方法即可。當廣播接收器接收到相應廣播時,就會執行onReceiver()方法。

在這裏我註冊一個監聽網絡狀態變化的廣播接收器做例子

首先,Android系統爲了保護用戶的安全和隱私,對App申請敏感權限有着嚴格的規定,必須在配置文件中聲明權限,否則會導致App的報錯。而我們需要訪問系統的網絡狀態也屬於敏感權限,是需要聲明權限的。

打開AndroidManifest.xml文件,在<manifest>標籤里加入以下代碼即可:

<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />

然後到註冊廣播接收器:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter filter = new IntentFilter();
        filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        NetworkStateReceiver networkStateReceiver = new NetworkStateReceiver();
        registerReceiver(networkStateReceiver, filter);
    }	
	
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(networkStateReceiver);
    }
	
    class NetworkStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceiver(Context context, Intent intent){
        ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isAvailable()) {
            Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
          }
        }
    }
}

在onReceive()方法中,首先通過getSystemService()方法得到了ConnectivityManager的實例,這是一個系統服務類,專門用於管理網絡連接,然後調用它的getActiveNetworkInfo()方法可以得到NetworkInfo的實例,接着調用NetworkInfo的isAvailable()方法,就可以判斷當前是否有網絡了。

2、靜態註冊廣播接收器

既然可以動態註冊廣播接收器了,而且動態註冊廣播接收器也比較靈活,爲什麼還需要靜態註冊廣播接收器,特意跑到AndroidManifest.xml中去註冊呢?這是因爲動態註冊廣播接收器的一個缺點:動態註冊的廣播接收器只能在程序啓動之後才能接收到廣播,因爲他是寫在oncreate()方法裏面的。

所以當我們需要程序在未啓動的情況下接受廣播的時候,比如我們希望程序能接受一條開機廣播的時候,就需要靜態註冊廣播接收器了。

下面我們來創建一個開機啓動的廣播接收器。

首先在MainActivity中創建廣播接收器:

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, “BootCompleteReceiver”, Toast.LENGTH_LONG).show();
    }
}

然後在AndroidManifest.xml文件中註冊這個廣播接收器:

在<application>標籤中添加以下代碼:

<receiver
    android:name=”.BootCompleteReceiver”
    android:enabled=”true”
    android:exported=”true”>
    <intent-filter>
        <action android:name=”android.intent.action.BOOT_COMPLETED” />
    </intent-filter>
</receiver>

其中enabled屬性表示是否啓用這個廣播接收器,exported屬性則表示是否允許這個廣播接收器接收本程序以外的廣播。標籤<intent-filter>的內容則爲廣播接收器可以接受的廣播。

另外,因爲監聽系統開機廣播也需要聲明權限,所以還需要在<manifest>標籤內加入權限聲明:

<uses-permission android:name=”android.permission.RECEIVE_BOOT_COMPLETED” />

這樣靜態註冊廣播接收器就完成了。

 

三、發送廣播

廣播主要分爲 標準廣播和有序廣播。

1、發送標準廣播

發送標準廣播比較簡單,只需要在想要發送廣播的地方添加以下代碼即可:

Intent intent = new Intent("android.intent.action.BOOT_COMPLETED");
sendBroadcast(intent);

2、發送有序廣播

廣播是一種可以跨進程的通信方式,就像前面提到的接收系統網絡狀態變化的廣播,我們應用程序內發送的廣播在其他應用也是可以接收到的。

而有序廣播則提供了一個更加靈活的發送廣播的方式給我們。

有序廣播的接收器可以通過設定優先級來決定哪個廣播接收器先接收廣播。優先級的數值越大優先級越高(取值範圍-1000~1000),優先級高的廣播接收器在接收到廣播並進行相應邏輯處理後可將廣播傳遞給下一個優先級高的接收器或者直接截斷廣播的傳遞;若優先級相同,那麼註冊時間較早的廣播接收器會優先收到廣播。

由此可見,相對於標準廣播,有序廣播在靈活性高的同時的效率就比較低了。

我們安卓手機中一個較爲常見的功能:短信攔截,就可以通過有序廣播來實現。

短信攔截原理:系統收到短信的時候,會發出一個有序廣播,然後短信攔截程序可以通過設置其本身短信廣播接收器的優先級來優先獲取到短信,對內容進行識別,若判定爲騷擾短信則截斷廣播的傳遞。

發送有序廣播和發送標準廣播基本一樣:

Intent intent = new Intent(“android.net.conn.CONNECTIVITY_CHANGE”);
sendOrderedBroadcast(intent, null);

sendOrderedBroadcast(intent, receiverPermission) 方法接收兩個參數:

  1. Intent:打算髮出的廣播,與此廣播匹配的廣播接收器將會響應
  2. reveiverPermission:權限字符串,可以引用系統值也可以自定義。若設定權限字符串後,則接收器也必須聲明該權限纔可以接收到該廣播;若爲null即不需要權限即可接收。

但是隻是發送有序廣播的話還不能達到有序的目的,如果不對相應的廣播接收器進行優先級的設定,那麼和標準廣播是一樣的,所有的廣播接收器會同時接收到這條廣播。

對廣播接收器設定優先級:

1)動態註冊廣播接收器:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
        filter.setPriority(1000);	//設置優先級
        AnotherReceiver anotherReceiver = new AnotherReceiver();
        registerReceiver(anotherReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(anotherReceiver);
    }

    class AnotherReceiver extends BroadcastReceiver {

        @Override
        public void onReceiver(Context context, Intent intent){
            Toast.makeToast(context, “AnotherReceiver”, Toast.LENGTH_SHORT).show();
            abortBroadcast();	    //截斷廣播的傳遞
        }
    }
}

動態註冊廣播接收器的話可以通過IntentFilter.setPriority()方法來設置優先級。如果想在這個廣播接收器接收到廣播後截斷廣播的傳遞,則可以在onReceiver()裏添加abortBroadcast()方法。

2)靜態註冊廣播接收器:

在AndroidManifest.xml文件<application>標籤中添以下代碼:

<receiver>
    android:name=”.AnotherReceiver”
    android:enabled=”true”
    android:exported=”true”>
    <intent-filter android:priority=”100”>
        <action android:name=”android.intent.action.BOOT_COMPLETED” />
    </intent-filter>
</receiver>

靜態註冊廣播接收器的話需要在<intent-filter>標籤裏添加android:priority=“100”的屬性,

另外如果需要截斷廣播的傳遞,同樣也是在onReceiver()裏添加abortBroadcast()方法:

class AnotherReceiver extends BroadcastReceiver {
	@Override
	public void onReceiver(Context context, Intent intent){
        Toast.makeToast(context, “AnotherReceiver”, Toast.LENGTH_SHORT).show();
        abortBroadcast();
	}
}

 

四、本地廣播

以上說到的標準廣播有序廣播都是全局廣播,發出的廣播任意一個程序都可以接收到,這就很容易導致安全性問題,比如有時候發送的廣播攜帶有關鍵性數據,有可能會被別的應用程序攔截。

爲了解決全局廣播的安全性問題,Android引進了本地廣播機制。本地廣播發出後只能夠在應用程序內部傳遞,也只有應用程序內部的接收器能接收到本地廣播,這樣廣播的安全性問題就能得到解決了。

本地廣播和全局廣播不同的地方在於本地廣播主要使用LocalBroadcastManager對廣播的發送、註冊、註銷進行管理。

下面寫一個本地廣播的例子:(界面設置一個按鈕,點擊按鈕即可發送本地廣播)

public class MainActivity extends AppCompatActivity {

    private LocalReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        localBroadcastManager = LocalBroadcastManager.getInstance(this);    //獲取實例

        button.setOnClickListener (new View.onClickListener) {
            @Override
            public void onClick (View v){
                Intent intent = new Intent("android.net.conn.CONNECTIVITY_CHANGE");
                localBroadcastManager.sendBroadcast(intent);    //發送本地廣播
            }
        }
        IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, filter);    //註冊本地廣播接收器
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }

    public class LocalReceiver extents BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received in LocalReceiver", Toast.LENGTH_SHORT),show();
        }
    }
}

從上面的例子可以看出,本地廣播的應用和前面的動態註冊廣播接收器和發送標準廣播差別並不大,只是本地廣播需要先通過LocalBroadcastManager.getInstance(this)方法來獲取一個LocalBroadcastManager的實例,然後在發送本地廣播、註冊廣播接收器和註銷廣播接收器的時候都是通過這個實例進行操作。

另外需要注意的是,本地廣播無法靜態註冊,因爲本地廣播是隻需要在程序內運行,如果靜態註冊的話(開機註冊)其實是完全沒有必要的。

本地廣播的優勢:

  1. 本地廣播只會在程序內發送,不會導致數據泄露等問題;
  2. 本地廣播接收器不會接收到其他程序發送的廣播,不會導致安全性問題;
  3. 本地廣播相對於標準廣播更加高效。

(注:此篇爲第一行代碼(第二版)的學習筆記)

 

 

 

 

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