android: 接收系統廣播

yufan發表於2016-02-03

Android 內建了很多系統級別的廣播,我們可以在應用程式中通過監聽這些廣播來得到 各種系統的狀態資訊。比如手機開機完成後會發出一條廣播,電池的電量發生變化會發出一 條廣播,時間或時區發生改變也會發出一條廣播等等。如果想要接收到這些廣播,就需要使 用廣播接收器,下面我們就來看一下它的具體用法。

5.2.1    動態註冊監聽網路變化

廣播接收器可以自由地對自己感興趣的廣播進行註冊,這樣當有相應的廣播發出時,廣 播接收器就能夠收到該廣播,並在內部處理相應的邏輯。註冊廣播的方式一般有兩種,在代 碼中註冊和在 AndroidManifest.xml 中註冊,其中前者也被稱為動態註冊,後者也被稱為靜態 註冊。

那麼該如何建立一個廣播接收器呢?其實只需要新建一個類,讓它繼承自 BroadcastReceiver, 並重寫父類的 onReceive()方法就行了。這樣當有廣播到來時,onReceive()方法就會得到執行, 具體的邏輯就可以在這個方法中處理。

那我們就先通過動態註冊的方式編寫一個能夠監聽網路變化的程式,藉此學習一下廣播 接收器的基本用法吧。新建一個 BroadcastTest 專案,然後修改 MainActivity 中的程式碼,如下 所示:

 public class MainActivity extends Activity {

private IntentFilter intentFilter;

private NetworkChangeReceiver networkChangeReceiver;

 

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter();

intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

networkChangeReceiver = new NetworkChangeReceiver();

registerReceiver(networkChangeReceiver, intentFilter);

}

 

@Override

protected void onDestroy() {

super.onDestroy();

unregisterReceiver(networkChangeReceiver);

}

 

class NetworkChangeReceiver extends BroadcastReceiver {

 

@Override

public void onReceive(Context context, Intent intent) {

 

Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();

}

}

 

}

可以看到,我們在 MainActivity 中定義了一個內部類 NetworkChangeReceiver,這個類 是繼承自 BroadcastReceiver 的,並重寫了父類的 onReceive()方法。這樣每當網路狀態發生變 化時,onReceive()方法就會得到執行,這裡只是簡單地使用 Toast 提示了一段文字資訊。

然後觀察 onCreate()方法,首先我們建立了一個 IntentFilter 的例項,並給它新增了一個 值為 android.net.conn.CONNECTIVITY_CHANGE 的 action,為什麼要新增這個值呢?因為 當網路狀態發生變化時,系統發出的正是一條值為 android.net.conn.CONNECTIVITY_ CHANGE 的廣播,也就是說我們的廣播接收器想要監聽什麼廣播,就在這裡新增相應的 action 就行了。接下來建立了一個 NetworkChangeReceiver 的例項,然後呼叫 registerReceiver() 方法進行註冊,將 NetworkChangeReceiver 的例項和 IntentFilter 的例項都傳了進去,這樣 NetworkChangeReceiver 就會收到所有值為 android.net.conn.CONNECTIVITY_CHANGE 的廣 播,也就實現了監聽網路變化的功能。

最後要記得,動態註冊的廣播接收器一定都要取消註冊才行,這裡我們是在 onDestroy()方法中通過呼叫 unregisterReceiver()方法來實現的。 整體來說,程式碼還是非常簡單的,現在執行一下程式。首先你會在註冊完成的時候收到一條廣播,然後按下 Home 鍵回到主介面(注意不能按 Back 鍵,否則 onDestroy()方法會執 行),接著按下 Menu 鍵→System settings→Data usage 進入到資料使用詳情介面,然後嘗試著 開關 Mobile Data 來啟動和禁用網路,你就會看到有 Toast 提醒你網路發生了變化。

不過只是提醒網路發生了變化還不夠人性化,最好是能準確地告訴使用者當前是有網路還 是沒有網路,因此我們還需要對上面的程式碼進行進一步的優化。修改 MainActivity 中的程式碼, 如下所示:

 

public class MainActivity extends Activity {

……

class NetworkChangeReceiver extends BroadcastReceiver {

 

@Override

public void onReceive(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()方法,就可以判斷 出當前是否有網路了,最後我們還是通過 Toast 的方式對使用者進行提示。

另外,這裡有非常重要的一點需要說明,Android 系統為了保證應用程式的安全性做了 規定,如果程式需要訪問一些系統的關鍵性資訊,必須在配置檔案中宣告許可權才可以,否則 程 序 將 會 直 接 崩 潰 , 比 如 這 裡 查 詢 系 統 的 網 絡 狀 態 就 是 需 要 聲 明 權 限 的 。 打 開 AndroidManifest.xml 檔案,在裡面加入如下許可權就可以查詢系統網路狀態了:

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"

android:versionCode="1" android:versionName="1.0" >

<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />

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

……

</manifest>

訪問 http://developer.android.com/reference/android/Manifest.permission.html 可以檢視 Android

系統所有可宣告的許可權。

現在重新執行程式,然後按下 Home 鍵→按下 Menu 鍵→System settings→Data usage 進 入到資料使用詳情介面,關閉 Mobile Data 會彈出無網路可用的提示,如圖 5.3 所示。

圖   5.3

 

然後重新開啟 Mobile Data 又會彈出網路可用的提示,如圖 5.4 所示。

 

圖   5.4

 

 

5.2.2    靜態註冊實現開機啟動

 

動態註冊的廣播接收器可以自由地控制註冊與登出,在靈活性方面有很大的優勢,但是 它也存在著一個缺點,即必須要在程式啟動之後才能接收到廣播,因為註冊的邏輯是寫在 onCreate()方法中的。那麼有沒有什麼辦法可以讓程式在未啟動的情況下就能接收到廣播 呢?這就需要使用靜態註冊的方式了。

這裡我們準備讓程式接收一條開機廣播,當收到這條廣播時就可以在 onReceive()方法裡 執行相應的邏輯,從而實現開機啟動的功能。新建一個 BootCompleteReceiver 繼承自 BroadcastReceiver,程式碼如下所示:

 

public class BootCompleteReceiver extends BroadcastReceiver {

 

@Override

public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();

}

 

}

可以看到,這裡不再使用內部類的方式來定義廣播接收器,因為稍後我們需要在 AndroidManifest.xml 中將這個廣播接收器的類名註冊進去。在 onReceive()方法中,還是簡單 地使用 Toast 彈出一段提示資訊。

然後修改 AndroidManifest.xml 檔案,程式碼如下所示:

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"

android:versionCode="1" android:versionName="1.0" >

……

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

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

<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >

……

<receiver android:name=".BootCompleteReceiver" >

<intent-filter>

<action android:name="android.intent.action.BOOT_COMPLETED" />

</intent-filter>

</receiver>

</application>

</manifest>

終於,<application>標籤內出現了一個新的標籤<receiver>,所有靜態註冊的廣播接收器 都是在這裡進行註冊的。它的用法其實和<activity>標籤非常相似,首先通過 android:name 來指定具體註冊哪一個廣播接收器,然後在<intent-filter>標籤里加入想要接收的廣播就行了, 由於 Android 系統啟動完成後會發出一條值為 android.intent.action.BOOT_COMPLETED 的廣 播,因此我們在這裡新增了相應的 action。

另外,監聽系統開機廣播也是需要宣告許可權的,可以看到,我們使用<uses-permission>標籤又加入了一條 android.permission.RECEIVE_BOOT_COMPLETED 許可權。 現在重新執行程式後,我們的程式就已經可以接收開機廣播了,首先開啟到應用程式管理介面來檢視一下當前程式所擁有的許可權。在桌面按下 Menu 鍵→System settings→Apps,然 後點選 BroadcastTest,如圖 5.5 所示。

 

圖   5.5

 

可以看到,我們的程式目前擁有訪問網路狀態和開機自動啟動的許可權。然後將模擬器關 閉並重新啟動,在啟動完成之後就會收到開機廣播了,如圖 5.6 所示。

圖   5.6

 

到目前為止,我們在廣播接收器的 onReceive()方法中都只是簡單地使用 Toast 提示了一 段文字資訊,當你真正在專案中使用到它的時候,就可以在裡面編寫自己的邏輯。需要注意 的是,不要在 onReceive()方法中新增過多的邏輯或者進行任何的耗時操作,因為在廣播接收 器中是不允許開啟執行緒的,當 onReceive()方法執行了較長時間而沒有結束時,程式就會報錯。 因此廣播接收器更多的是扮演一種開啟程式其他元件的角色,比如建立一條狀態列通知,或 者啟動一個服務等.

相關文章