天天看點

Android的IPC機制(六)—— BroadcastReceiver的使用綜述生命周期使用方法總結源碼下載下傳

綜述

  在Android的四大元件中除了ContentProvider能夠用于程序間的通信外,還有一個也能夠用于程序間的通信,那就是BroadcastReceiver。BroadcastReceiver翻譯成中文為廣播接收器,既然作為廣播接收器,那麼必然就有Broadcast。在Android中,Broadcast是一種廣泛運用的在應用程式之間傳輸資訊的機制。而BroadcastReceiver則是對發送出來的 Broadcast進行過濾接受并響應的一類元件。在 Android 裡面有各種各樣的廣播,比如電池的使用狀态,電話的接收和短信的接收都會産生一個廣播,應用程式開發者也可以監聽這些廣播并做出程式邏輯的處理。

生命周期

  對于BroadcastReceiver的生命周期也是非常的簡單。它的生命周期隻存在于onReceive方法中。對于這個onReceive方法它也是運作在主線程中。是以在onReceive方法中不能進行的耗時的操作。否則就會出現ANR(Application Not Responding)。由于這個ANR的限制對于onReceive方法最多可執行的時間為10秒左右。并且由于BroadcastReceiver的生命周期随着onReceive方法的結束而結束。是以我們不能再onReceive方法中去建立一個線程來執行任務。因為onReceive方法執行完畢,這時候這個BroadcastReceiver 也就結束了。也就無法處理異步的結果。如果這個BroadcastReceiver在獨立的程序中,它所在程序也很容易在系統需要記憶體時被優先殺死 , 因為它屬于空程序 ( 沒有任何活動元件的程序 ). 那麼正在工作的子線程也會被殺死 。若是我們需要完成一個比較耗時任務的話,我們可以通過發送Intent給Service,并且由這個Service來完成這項任務。

使用方法

注冊方式

  對于BroadcastReceiver有兩種注冊方式,一種是動态注冊,一種是靜态注冊。靜态注冊就是在AndroidManifest檔案中進行注冊。而動态注冊則是在程式中通過registerReceiver方法注冊。對于這兩種注冊方法的優先級來說,動态注冊的優先級要高于靜态注冊的優先級。那麼下面我們來看一下這兩種注冊方式。

  1. 靜态注冊

<receiver
    android:name=".MyReceiver">
    <intent-filter>
        <action android:name="com.example.ljd.BROADCASTRECEIVER" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>
           

  2. 動态注冊

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.example.ljd.BROADCASTRECEIVER");
registerReceiver(myReceiver,intentFilter);
           

  對于動态注冊,當我們不在使用的這個broadcastreceiver的時候我們還需要對它解除注冊。

unregisterReceiver(myReceiver);
           

廣播類型

  在這裡我們介紹一下其中最常用的兩種廣播:普通廣播和有序廣播。它們分别是通過sendBroadcast和sendOrderedBroadcast進行發送。那麼現在我們就來看一下這兩種方式的用法與差別。

普通廣播(Normal Broadcast)

  我們可以通過sendBroadcast方法發送一個普通廣播。對于普通廣播來說,BroadcastReceiver的優先級一樣的話它們接收廣播先後順序是随機的,并且在廣播的傳輸過程中我們無法截斷廣播。

  下面我們做一個實驗。我們建立三個BroadcastReceiver,使其運作在三個不同的程序内。并且我們采用靜态注冊方式。

  第一個BroadcastReceiver

package com.example.ljd.broadcastreceiver;

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

public class MyReceiver1 extends BroadcastReceiver {

    private final String TAG = "MyReceiver1";
    public MyReceiver1() {
    }
    @Override
    public void onReceive(Context context, Intent intent) {

        if (Constant.BROADCAST_ACTION.equals(intent.getAction())){
            Log.d(TAG,intent.getStringExtra(Constant.CONFERENCE_KEY));
        }
    }
}
           

  第二個BroadcastReceiver

package com.example.ljd.broadcastreceiver;

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

public class MyReceiver2 extends BroadcastReceiver {
    private final String TAG = "MyReceiver2";
    public MyReceiver2() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Constant.BROADCAST_ACTION.equals(intent.getAction())){
            Log.d(TAG, intent.getStringExtra(Constant.CONFERENCE_KEY));
        }
    }
}
           

  第三個BroadcastReceiver

package com.example.ljd.broadcastreceiver;

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

public class MyReceiver3 extends BroadcastReceiver {
    private final String TAG = "MyReceiver3";
    public MyReceiver3() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Constant.BROADCAST_ACTION.equals(intent.getAction())){
            Log.d(TAG,intent.getStringExtra(Constant.CONFERENCE_KEY));
        }
    }
}
           

  對三個BroadcastReceiver進行靜态注冊。

<receiver
    android:name=".MyReceiver1"
    android:process=":receiver1">
    <intent-filter>
        <action android:name="com.example.ljd.BROADCASTRECEIVER" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>
<receiver
    android:name=".MyReceiver3"
    android:process=":receiver3">
    <intent-filter>
        <action android:name="com.example.ljd.BROADCASTRECEIVER" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>
<receiver
    android:name=".MyReceiver2"
    android:process=":receiver2">
    <intent-filter>
        <action android:name="com.example.ljd.BROADCASTRECEIVER" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>
           

  我們在建立一個Activity,在這個Activity裡面我們建立一個BroadcastReceiver并且對其進行動态注冊。然後我們采用sendBroadcast進行發送廣播。下面看一下Activity代碼。

package com.example.ljd.broadcastreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity {

    private final String TAG = "MainActivity";

    private MyReceiver myReceiver = new MyReceiver();

    class MyReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            if (Constant.BROADCAST_ACTION.equals(intent.getAction())){
                Log.d(TAG,intent.getStringExtra(Constant.CONFERENCE_KEY));
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);
        ButterKnife.bind(this);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Constant.BROADCAST_ACTION);
        registerReceiver(myReceiver,intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ButterKnife.bind(this);
        unregisterReceiver(myReceiver);
    }

    @OnClick(R.id.send_button)
    public void onClickButton(){
        Intent intent = new Intent(Constant.BROADCAST_ACTION);
        intent.putExtra(Constant.CONFERENCE_KEY,"你好,明天九點半101會議室開會。");
        sendBroadcast(intent);
    }
}
           

  我們發送一條通知開會的廣播,并且接收到廣播後列印出來。由于代碼非常簡單這裡就不在進行說明,從下面的結果我們可以看出,無論是采用靜态注冊還是動态注冊,它們接收到的先後順序是随機的。我們發送了三次廣播。這時候可以通過DDMS檢視一下日志資訊。  

結果1:

Android的IPC機制(六)—— BroadcastReceiver的使用綜述生命周期使用方法總結源碼下載下傳

結果2:

Android的IPC機制(六)—— BroadcastReceiver的使用綜述生命周期使用方法總結源碼下載下傳

結果3:

Android的IPC機制(六)—— BroadcastReceiver的使用綜述生命周期使用方法總結源碼下載下傳

  現在我們對MyReceiver2中的OnReceive添加一個abortBroadcast方法,這個方法是用來截斷廣播的。然後我們在看一下運作結果

Android的IPC機制(六)—— BroadcastReceiver的使用綜述生命周期使用方法總結源碼下載下傳

  我們可以看到雖然系統給我們抛出了了運作時異常,但是我們還是準确無誤的接收到了廣播。而這個異常是在abortBroadcast裡面的checkSynchronousHint方法中抛出的,這個checkSynchronousHint方法是用來檢測廣播接收者是否同步接收廣播。由于我們接收廣播是随機的,是以抛出運作時異常。

  現在又有一種情形,如果我們隻是希望動态建立的BroadcastReceiver進行接收廣播,而靜态建立的BroadcastReceiver不進行接收。我們可以在發送廣播的Intent中設定一個flag:FLAG_RECEIVER_REGISTERED_ONLY。

  這時候我們可以看到隻有動态注冊的廣播才接收到消息。

Android的IPC機制(六)—— BroadcastReceiver的使用綜述生命周期使用方法總結源碼下載下傳

有序廣播(Ordered Broadcast)

  對于有序廣播而言,BroadcastReceiver是按照接收者的優先級接收廣播 , 優先級在 intent-filter 中的 priority 中聲明 。priority的值從-1000 到1000 之間 , 值越大 , 優先級越高。如果我們沒有設定priority的話,那麼對于BroadcastReceiver的優先級則是先注冊的要大于後注冊的,動态注冊優先級大于靜态注冊的優先級。對于剛才的例子來說在有序廣播中它們的優先級就是MyReceiver>MReceiver1>MReceiver3>MReceiver2。并且在在有序廣播中我們可以終止廣播 ,接收者也能夠篡改内容。

  我們可以通過sendOrderedBroadcast進行發送一個有序廣播,他有兩個重載方法。

public void sendOrderedBroadcast(Intent intent,
            String receiverPermission)

public void sendOrderedBroadcast(
        Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,
        Handler scheduler, int initialCode, String initialData,
        Bundle initialExtras)
           

這裡我們對裡面的參數進行一下說明: 

1. intent:所有比對的這個intent的BroadcastReceiver将接收廣播。

2. receiverPermission:這個是權限,一個接收器必須持有接收我們的廣播的權限。如果BroadcastReceiver沒有設定任何權限,這裡為null即可。

3. resultReceiver :設定一個BroadcastReceiver 來當作最後的廣播接收器。

4. initialCode:結果代碼的初始值。通常為 Activity.RESULT_OK ,這個值是 -1 。也可以為其他 int 型,如 0,1,2 ;

5. initialData: 一種結果資料的初始值。為String 類型 ;

6. initialExtras:一種結果額外的初始值。為Bundle類型。

  

  對于我們沒有設定priority屬性的BroadcastReceiver的優先級情況很簡單這裡也就不在進行驗證。現在我們來修改上面代碼來看一下是如何攔截廣播并且修改廣播中資料的。我們首先對動态注冊的優先級設為0。對上面的MyReceiver1,MyReceiver2,MyReceiver3的優先級分别設為3,2,1。然後我們通過sendOrderedBroadcast發送一個有序廣播。

Intent intent = new Intent(Constant.BROADCAST_ACTION);
intent.putExtra(Constant.CONFERENCE_KEY, "你好,明天九點半101會議室開會。");

Bundle bundle = new Bundle();
bundle.putString(Constant.DINE_KEY, "今天晚上聚餐");
intent.putExtras(bundle);
sendOrderedBroadcast(intent,null,null,null, Activity.RESULT_OK ,null,bundle);
           

  在MyReceiver1的onReceive方法中修改如下:

if (Constant.BROADCAST_ACTION.equals(intent.getAction())){
     Log.d(TAG, intent.getStringExtra(Constant.CONFERENCE_KEY));
     Log.d(TAG, getResultExtras(true).getString(Constant.DINE_KEY));
     Bundle bundle = getResultExtras(true);
     bundle.putString(Constant.DINE_KEY, "後天中午一起聚餐");
     setResultExtras(bundle);
 }
           

  在MyReceiver2的onReceive方法中修改如下:

if (Constant.BROADCAST_ACTION.equals(intent.getAction())){
    Log.d(TAG, intent.getStringExtra(Constant.CONFERENCE_KEY));
    Log.d(TAG, getResultExtras(true).getString(Constant.DINE_KEY));
    abortBroadcast();
}
           

  getResultExtras:優先級低的BroadcastReceiver可以通過這個方法擷取到最新的經過處理的資訊集合。

  下面我們來看一下運作結果:

Android的IPC機制(六)—— BroadcastReceiver的使用綜述生命周期使用方法總結源碼下載下傳

  在這裡我們可以看到由于在MyReceiver2中的onReceive方法中設定了abortBroadcast,比MyReceiver2優先級低的BroadcastReceiver接收不到廣播。并且在MyReceiver1中将“今天晚上聚餐”成功修改為“後天中午聚餐”。

總結

  BroadcastReceiver除了可以接收我們自己發送的廣播以外,還能夠接收一些系統的廣播。例如對網絡環境的監測,手機電量的監測,裝置重新開機的監測等等。也就是說BroadcastReceiver可以友善應用程式和系統,應用程式之間,應用程式内的通信。是以對于我們單個應用程式來說它是存在安全性的問題。不過我們我們可以在發送廣播時指定接收者必須具備的permission。當然我們也可以通過使用LocalBroadcastManager來解決安全性問題。對于LocalBroadcastManager使用也很簡單。它是一個單例模式,可以通過LocalBroadcastManager.getInstance(context)擷取LocalBroadcastManager對象。然後通過LocalBroadcastManager裡面的registerReceiver方法進行BroadcastReceiver進行注冊。也就是說使用LocalBroadcastManager必須進行動态注冊。

源碼下載下傳