BroadCastReceiver

BroadcastReceiverhtml

  在Android中,Broadcast是一種普遍運用的在應用程序之間傳輸信息的機制。而BroadcastReceiver是對發送出來的 Broadcast進行過濾接受並響應的一類組件。java

下面將詳細的闡述如何發送Broadcast和使用BroadcastReceiver過濾接收的過程:android

  首先在須要發送信息的地方,把要發送的信息和用於過濾的信息(如Action、Category)裝入一個Intent對象,而後經過調用 sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent對象以廣播方式發送出去。安全

  當Intent發送之後,全部已經註冊的BroadcastReceiver會檢查註冊時的IntentFilter是否與發送的Intent相匹配,若匹配則就會調用BroadcastReceiver的onReceive()方法。因此當咱們定義一個BroadcastReceiver的時候,都須要實現onReceive()方法。網絡

  註冊BroadcastReceiver有兩種方式:app

  靜態註冊:在AndroidManifest.xml中用標籤生命註冊,並在標籤內用標籤設置過濾器。異步

  <receiver android:name="myRecevice"> //繼承BroadcastReceiver,重寫onReceiver方法ide

    <intent-filter>測試

      <action android:name="com.dragon.net"></action> //使用過濾器,接收指定action廣播.net

    </intent-filter>

  </receiver>

  動態註冊:

  IntentFilter intentFilter = new IntentFilter();

  intentFilter.addAction(String); //爲BroadcastReceiver指定action,使之用於接收同action的廣播

registerReceiver(BroadcastReceiver,intentFilter);

  通常:在onStart中註冊,onStop中取消unregisterReceiver

  指定廣播目標Action:Intent intent = new Intent(actionString);

  而且可經過Intent攜帶消息 :intent.putExtra("msg", "hi,我經過廣播發送消息了");

  發送廣播消息:Context.sendBroadcast(intent )

BroadcastReceiver也就是「廣播接收者」的意思,顧名思義,它就是用來接收來自系統和應用中的廣播。

在Android系統中,廣播體如今方方面面,例如當開機完成後系統會產生一條廣播,接收到這條廣播就能實現開機啓動服務的功能;當網絡狀態改變時系統會產生一條廣播,接收到這條廣播就能及時地作出提示和保存數據等操做;當電池電量改變時,系統會產生一條廣播,接收到這條廣播就能在電量低時告知用戶及時保存進度,等等。

Android中的廣播機制設計的很是出色,不少事情本來須要開發者親自操做的,如今只需等待廣播告知本身就能夠了,大大減小了開發的工做量和開發週期。而做爲應用開發者,就須要數練掌握Android系統提供的一個開發利器,那就是BroadcastReceiver。下面咱們就對BroadcastReceiver逐一地分析和演練,瞭解和掌握它的各類功能和用法。

首先,咱們來演示一下建立一個BroadcastReceiver,並讓這個BroadcastReceiver可以根據咱們的須要來運行。

要建立本身的BroadcastReceiver對象,咱們須要繼承android.content.BroadcastReceiver,並實現其onReceive方法。下面咱們就建立一個名爲MyReceiver廣播接收者:

[java] view plaincopy

package com.scott.receiver;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;  
  
public class MyReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "MyReceiver";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = intent.getStringExtra("msg");  
        Log.i(TAG, msg);  
    }  
  
}

在onReceive方法內,咱們能夠獲取隨廣播而來的Intent中的數據,這很是重要,就像無線電同樣,包含不少有用的信息。

在建立完咱們的BroadcastReceiver以後,還不可以使它進入工做狀態,咱們須要爲它註冊一個指定的廣播地址。沒有註冊廣播地址的BroadcastReceiver就像一個缺乏選臺按鈕的收音機,雖然功能俱備,但也沒法收到電臺的信號。下面咱們就來介紹一下如何爲BroadcastReceiver註冊廣播地址。

靜態註冊

靜態註冊是在AndroidManifest.xml文件中配置的,咱們就來爲MyReceiver註冊一個廣播地址:

[html] view plaincopy

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

配置了以上信息以後,只要是android.intent.action.MY_BROADCAST這個地址的廣播,MyReceiver都可以接收的到。注意,這種方式的註冊是常駐型的,也就是說當應用關閉後,若是有廣播信息傳來,MyReceiver也會被系統調用而自動運行。

動態註冊

動態註冊須要在代碼中動態的指定廣播地址並註冊,一般咱們是在Activity或Service註冊一個廣播,下面咱們就來看一下注冊的代碼:

[java] view plaincopy

MyReceiver receiver = new MyReceiver();  
          
IntentFilter filter = new IntentFilter();  
filter.addAction("android.intent.action.MY_BROADCAST");  
          
registerReceiver(receiver, filter);

注意,registerReceiver是android.content.ContextWrapper類中的方法,Activity和Service都繼承了ContextWrapper,因此能夠直接調用。在實際應用中,咱們在Activity或Service中註冊了一個BroadcastReceiver,當這個Activity或Service被銷燬時若是沒有解除註冊,系統會報一個異常,提示咱們是否忘記解除註冊了。因此,記得在特定的地方執行解除註冊操做:

[java] view plaincopy

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

執行這樣行代碼就能夠解決問題了。注意,這種註冊方式與靜態註冊相反,不是常駐型的,也就是說廣播會跟隨程序的生命週期。

咱們能夠根據以上任意一種方法完成註冊,當註冊完成以後,這個接收者就能夠正常工做了。咱們能夠用如下方式向其發送一條廣播:

[java] view plaincopy

public void send(View view) {  
    Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
    intent.putExtra("msg", "hello receiver.");  
    sendBroadcast(intent);  
}

注意,sendBroadcast也是android.content.ContextWrapper類中的方法,它能夠將一個指定地址和參數信息的Intent對象以廣播的形式發送出去。

點擊發送按鈕,執行send方法,控制檯打印以下:

看到這樣的打印信息,代表咱們的廣播已經發出去了,而且被MyReceiver準確無誤的接收到了。

上面的例子只是一個接收者來接收廣播,若是有多個接收者都註冊了相同的廣播地址,又會是什麼狀況呢,能同時接收到同一條廣播嗎,相互之間會不會有干擾呢?這就涉及到普通廣播和有序廣播的概念了。

普通廣播(Normal Broadcast)

普通廣播對於多個接收者來講是徹底異步的,一般每一個接收者都無需等待便可以接收到廣播,接收者相互之間不會有影響。對於這種廣播,接收者沒法終止廣播,即沒法阻止其餘接收者的接收動做。

爲了驗證以上論斷,咱們新建三個BroadcastReceiver,演示一下這個過程,FirstReceiver、SecondReceiver和ThirdReceiver的代碼以下:

[java] view plaincopy

package com.scott.receiver;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;  
  
public class FirstReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "NormalBroadcast";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = intent.getStringExtra("msg");  
        Log.i(TAG, "FirstReceiver: " + msg);  
    }  
  
}

[java] view plaincopy

public class SecondReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "NormalBroadcast";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = intent.getStringExtra("msg");  
        Log.i(TAG, "SecondReceiver: " + msg);  
    }  
  
}

[java] view plaincopy

public class ThirdReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "NormalBroadcast";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = intent.getStringExtra("msg");  
        Log.i(TAG, "ThirdReceiver: " + msg);  
    }  
  
}

而後再次點擊發送按鈕,發送一條廣播,控制檯打印以下:

看來這三個接收者都接收到這條廣播了,咱們稍微修改一下三個接收者,在onReceive方法的最後一行添加如下代碼,試圖終止廣播:

[java] view plaincopy

abortBroadcast();

再次點擊發送按鈕,咱們會發現,控制檯中三個接收者仍然都打印了本身的日誌,代表接收者並不能終止廣播。

有序廣播(Ordered Broadcast)

有序廣播比較特殊,它每次只發送到優先級較高的接收者那裏,而後由優先級高的接受者再傳播到優先級低的接收者那裏,優先級高的接收者有能力終止這個廣播。

爲了演示有序廣播的流程,咱們修改一下上面三個接收者的代碼,以下:

[java] view plaincopy

package com.scott.receiver;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.os.Bundle;  
import android.util.Log;  
  
public class FirstReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "OrderedBroadcast";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = intent.getStringExtra("msg");  
        Log.i(TAG, "FirstReceiver: " + msg);  
          
        Bundle bundle = new Bundle();  
        bundle.putString("msg", msg + "@FirstReceiver");  
        setResultExtras(bundle);  
    }  
  
}

[java] view plaincopy

public class SecondReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "OrderedBroadcast";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = getResultExtras(true).getString("msg");  
        Log.i(TAG, "SecondReceiver: " + msg);  
          
        Bundle bundle = new Bundle();  
        bundle.putString("msg", msg + "@SecondReceiver");  
        setResultExtras(bundle);  
    }  
  
}

[java] view plaincopy

public class ThirdReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "OrderedBroadcast";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        String msg = getResultExtras(true).getString("msg");  
        Log.i(TAG, "ThirdReceiver: " + msg);  
    }  
  
}

咱們注意到,在FirstReceiver和SecondReceiver中最後都使用了setResultExtras方法將一個Bundle對象設置爲結果集對象,傳遞到下一個接收者那裏,這樣以來,優先級低的接收者能夠用getResultExtras獲取到最新的通過處理的信息集合。

代碼改完以後,咱們須要爲三個接收者註冊廣播地址,咱們修改一下AndroidMainfest.xml文件:

[html] view plaincopy

<receiver android:name=".FirstReceiver">  
    <intent-filter android:priority="1000">  
        <action android:name="android.intent.action.MY_BROADCAST"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
<receiver android:name=".SecondReceiver">  
    <intent-filter android:priority="999">  
        <action android:name="android.intent.action.MY_BROADCAST"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
<receiver android:name=".ThirdReceiver">  
    <intent-filter android:priority="998">  
        <action android:name="android.intent.action.MY_BROADCAST"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>

咱們看到,如今這三個接收者的<intent-filter>多了一個android:priority屬性,而且依次減少。這個屬性的範圍在-1000到1000,數值越大,優先級越高。

如今,咱們須要修改一下發送廣播的代碼,以下:

[java] view plaincopy

public void send(View view) {  
    Intent intent = new Intent("android.intent.action.MY_BROADCAST");  
    intent.putExtra("msg", "hello receiver.");  
    sendOrderedBroadcast(intent, "scott.permission.MY_BROADCAST_PERMISSION");  
}

注意,使用sendOrderedBroadcast方法發送有序廣播時,須要一個權限參數,若是爲null則表示不要求接收者聲明指定的權限,若是不爲null,則表示接收者若要接收此廣播,需聲明指定權限。這樣作是從安全角度考慮的,例如系統的短信就是有序廣播的形式,一個應用多是具備攔截垃圾短信的功能,當短信到來時它能夠先接受到短信廣播,必要時終止廣播傳遞,這樣的軟件就必須聲明接收短信的權限。

因此咱們在AndroidMainfest.xml中定義一個權限:

[html] view plaincopy

<permission android:protectionLevel="normal"  
            android:name="scott.permission.MY_BROADCAST_PERMISSION" />

而後聲明使用了此權限:

[html] view plaincopy

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

關於這部分若是有不明白的地方能夠參考我以前寫過的一篇文章:Android聲明和使用權限

而後咱們點擊發送按鈕發送一條廣播,控制檯打印以下:

咱們看到接收是按照順序的,第一個和第二個都在結果集中加入了本身的標記,而且向優先級低的接收者傳遞下去。

既然是順序傳遞,試着終止這種傳遞,看一看效果如何,咱們修改FirstReceiver的代碼,在onReceive的最後一行添加如下代碼:

[java] view plaincopy

abortBroadcast();

而後再次運行程序,控制檯打印以下:

這次,只有第一個接收者執行了,其它兩個都沒能執行,由於廣播被第一個接收者終止了。

上面就是BroadcastReceiver的介紹,下面我將會舉幾個常見的例子加深一下你們對廣播的理解和應用:

1.開機啓動服務

咱們常常會有這樣的應用場合,好比消息推送服務,須要實現開機啓動的功能。要實現這個功能,咱們就能夠訂閱系統「啓動完成」這條廣播,接收到這條廣播後咱們就能夠啓動本身的服務了。咱們來看一下BootCompleteReceiver和MsgPushService的具體實現:

[java] view plaincopy

package com.scott.receiver;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;  
  
public class BootCompleteReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "BootCompleteReceiver";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Intent service = new Intent(context, MsgPushService.class);  
        context.startService(service);  
        Log.i(TAG, "Boot Complete. Starting MsgPushService...");  
    }  
  
}

[java] view plaincopy

package com.scott.receiver;  
  
import android.app.Service;  
import android.content.Intent;  
import android.os.IBinder;  
import android.util.Log;  
  
public class MsgPushService extends Service {  
  
    private static final String TAG = "MsgPushService";  
      
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.i(TAG, "onCreate called.");  
    }  
      
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.i(TAG, "onStartCommand called.");  
        return super.onStartCommand(intent, flags, startId);  
    }  
  
    @Override  
    public IBinder onBind(Intent arg0) {  
        return null;  
    }  
}

而後咱們須要在AndroidManifest.xml中配置相關信息:

[html] view plaincopy

<!-- 開機廣播接受者 -->  
<receiver android:name=".BootCompleteReceiver">  
    <intent-filter>  
        <!-- 註冊開機廣播地址-->  
        <action android:name="android.intent.action.BOOT_COMPLETED"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>  
<!-- 消息推送服務 -->  
<service android:name=".MsgPushService"/>

咱們看到BootCompleteReceiver註冊了「android.intent.action.BOOT_COMPLETED」這個開機廣播地址,從安全角度考慮,系統要求必須聲明接收開機啓動廣播的權限,因而咱們再聲明使用下面的權限:

[html] view plaincopy

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

通過上面的幾個步驟以後,咱們就完成了開機啓動的功能,將應用運行在模擬器上,而後重啓模擬器,控制檯打印以下:

若是咱們查看已運行的服務就會發現,MsgPushService已經運行起來了。

2.網絡狀態變化

在某些場合,好比用戶瀏覽網絡信息時,網絡忽然斷開,咱們要及時地提醒用戶網絡已斷開。要實現這個功能,咱們能夠接收網絡狀態改變這樣一條廣播,當由鏈接狀態變爲斷開狀態時,系統就會發送一條廣播,咱們接收到以後,再經過網絡的狀態作出相應的操做。下面就來實現一下這個功能:

[java] view plaincopy

package com.scott.receiver;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.net.ConnectivityManager;  
import android.net.NetworkInfo;  
import android.util.Log;  
import android.widget.Toast;  
  
public class NetworkStateReceiver extends BroadcastReceiver {  
      
    private static final String TAG = "NetworkStateReceiver";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        Log.i(TAG, "network state changed.");  
        if (!isNetworkAvailable(context)) {  
            Toast.makeText(context, "network disconnected!", 0).show();  
        }  
    }  
      
    /** 
     * 網絡是否可用 
     *  
     * @param context 
     * @return 
     */  
    public static boolean isNetworkAvailable(Context context) {  
        ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);  
        NetworkInfo[] info = mgr.getAllNetworkInfo();  
        if (info != null) {  
            for (int i = 0; i < info.length; i++) {  
                if (info[i].getState() == NetworkInfo.State.CONNECTED) {  
                    return true;  
                }  
            }  
        }  
        return false;  
    }  
  
}

再註冊一下這個接收者的信息:

[html] view plaincopy

<receiver android:name=".NetworkStateReceiver">  
    <intent-filter>  
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</receiver>

由於在isNetworkAvailable方法中咱們使用到了網絡狀態相關的API,因此須要聲明相關的權限才行,下面就是對應的權限聲明:

[html] view plaincopy

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

咱們能夠測試一下,好比關閉WiFi,看看有什麼效果。

3.電量變化

若是咱們閱讀軟件,多是全屏閱讀,這個時候用戶就看不到剩餘的電量,咱們就能夠爲他們提供電量的信息。要想作到這一點,咱們須要接收一條電量變化的廣播,而後獲取百分比信息,這聽上去挺簡單的,咱們就來實現如下:

[java] view plaincopy

package com.scott.receiver;  
  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.os.BatteryManager;  
import android.util.Log;  
  
public class BatteryChangedReceiver extends BroadcastReceiver {  
  
    private static final String TAG = "BatteryChangedReceiver";  
      
    @Override  
    public void onReceive(Context context, Intent intent) {  
        int currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);  //當前電量  
        int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);      //總電量  
        int percent = currLevel * 100 / total;  
        Log.i(TAG, "battery: " + percent + "%");  
    }  
  
}

而後再註冊一下廣播接地址信息就能夠了:

[html] view plaincopy

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

固然,有些時候咱們是要當即獲取電量的,而不是等電量變化的廣播,好比當閱讀軟件打開時當即顯示出電池電量。咱們能夠按如下方式獲取:

[java] view plaincopy

Intent batteryIntent = getApplicationContext().registerReceiver(null,  
        new IntentFilter(Intent.ACTION_BATTERY_CHANGED));  
int currLevel = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);  
int total = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);  
int percent = currLevel * 100 / total;  
Log.i("battery", "battery: " + percent + "%");
相關文章
相關標籤/搜索