1、廣播機制簡介:
因為Android中的每個應用程序都可以對自己感興趣的廣播盡心注冊,這樣程序只會接收自己所關心的廣播內容,
這些廣播來自于系統的,也可能來自于其他應用程序的。Android提供了一套完整的API,允許應用程序自己地發送和
接收廣播,發送廣播的方法就是借助Intent,而接收廣播的方法,要引入廣播接收器(Broadcast Receiver)。
廣播分為兩類,標準廣播和有序廣播,
標準廣播(Normal broadcase)是一種完全異步執行的廣播,再廣播發出去以后,
所有的廣播接收器機會會在同一時刻接收到這條廣播消息,因此它們之間沒有任何先后順序,這樣的廣播的效率會比較高
但同時也意味著它是無法被截斷的。‘
有序廣播(Ordered broadcasts)是一種同步執行的廣播,在廣播發出去之后,同一時刻只會有一個廣播接收器能
夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢后,廣播才會繼續傳遞。優先級高的廣播就可以先接收廣播消息,并且還可以截斷正在傳遞的廣播。
二、接收系統廣播
Android內置了很多系統級別的廣播,我們可以在應用程序中通過監聽這些廣播來得到各種系統的狀態信息。
1、動態注冊監聽網絡變化
廣播接收器可以自由的對自己感興趣的廣播進行注冊,當有相應的廣播發出時,廣播接收器就可以收到該廣播,并在
內部處理相應的邏輯。注冊廣播的方式一般有兩種 ,動態注冊和靜態注冊,所謂動態注冊是在代碼中注冊,靜態注冊在
AndroidManifest.xml中注冊。
如何創建一個廣播接收器呢? 只需要新建一個類,讓它繼承自BroadcastReceiver,并重寫父類的onReceive()方法
就行了。這樣當有廣播到來時, onReceive()方法就會得到執行,具體的邏輯就會在這個方法中處理。
我們先通過 動態注冊的方式去編寫一個能夠監聽網絡變化的程序,學習一下廣播接收器的基本用法。
新建項目BroadcastTest。然后,修改MainActivity中的代碼:
package com.example.broadcasttest;
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.widget.Toast;
public class MainActivity extends AppCompatActivity {
? ??
? ??
? ? private IntentFilter intentfiletr;
? ? private NetworkChangeReceiver networkChangeReceiver;
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? intentfiletr = new IntentFilter();
? ? ? ? intentfiletr.addAction("android.net.conn.CONNECTIVITY_CHANGE");
? ? ? ? networkChangeReceiver = new NetworkChangeReceiver();
? ? ? ? registerReceiver(networkChangeReceiver,intentfiletr);
? ? }
? ? @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提示了一段文本信息。
在onCreat()方法,我們首先創建了一個IntenFilter的實例,并給它添加了一個android.net.conn.CONNECTIVITY_CHANGE的
廣播,也就是說當我們想要監聽什么樣的廣播,就在這里添加相應的action。接下來,我們創建一個NetworkChangeReceiver
的實例,然后調用registerReceiver()方法進行注冊,將NetworkChangeReciver的實例和IntentFilter的實例都傳了進去
這樣,NetworkChangeReceiver就會收到所有值為android.conn.CONNECTIVITY_CHANGE的廣播。也就實現了監聽網絡
變化的功能。
最后,動態注冊的廣播接收器一定要取消注冊才行。我們是在onDestroy()方法中通過調用unregisterReceiver()來實現。
總結:
如何創建一個廣播接收器呢?廣播接收器如何接收廣播呢?
1、只需要新建一個類,讓它繼承自BroadcastReceiver,并重寫父類的onReceive()方法
就行了。這樣當有廣播到來時,onReceiver方法就會得到執行,具體的邏輯就會在這個方法中處理。然后創建這個類的
實例,調用其registerReceive()方法進行注冊,最后也可以在onDestroy()方法取消注冊。
2、發送廣播的方法是借助Intent。怎么接收廣播呢?通過創建一個IntentFilter的實例,往里面添加相應的action,
就可以接收對應的廣播。
運行代碼之后,,在注冊完成的時候,會收到一條廣播,然后按下Home鍵,回到主頁面,接著打開Setting程序->
Data usage 進入到數據使用詳情界面,然后嘗試著開關Cellular data 按鈕來啟動和禁用網絡,看到有Toast提醒網絡
發生了變化。
最后可以準確告訴用戶當前是有網絡還是無網絡,我們進一步優化代碼:
package com.example.broadcasttest;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;public class MainActivity extends AppCompatActivity {private IntentFilter intentfiletr;private NetworkChangeReceiver networkChangeReceiver;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);intentfiletr = new IntentFilter();intentfiletr.addAction("android.net.conn.CONNECTIVITY_CHANGE");networkChangeReceiver = new NetworkChangeReceiver();registerReceiver(networkChangeReceiver,intentfiletr);}@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(networkChangeReceiver);}class NetworkChangeReceiver extends BroadcastReceiver{@Overridepublic 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方法中,首先通過getSystemSerive()方法得到ConnectivityManager的實例,這是一個系統服務類,專門
用來管理網絡連接的。然后調用它的getAcitiveNetworkInfo()方法得到NetworkInfo的實例,接著調用NetworkInfo的
isAvailable()方法,就可以判斷當前是否有網絡了,最后我們還是通過Toast的方式對用戶進行提示。
Android系統為了保護用戶設備的安全和隱私,做了嚴格的規定,如何程序需要做一些對用戶來說比較敏感的操作,就必須在
配置文件中聲明權限才可以,否則程序將會直接崩潰。打開AndroidManifest.xml文件,在下面加入如下權限就可以訪問
系統網絡狀態了:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
? ? package="com.example.broadcasttest">
? ? ......
? ? <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
......
</manifest>
Android有很多操作都是需要聲明權限才可以進行的。
現在我們又可以重新運行程序了。
2、靜態注冊實現開機啟動
動態注冊的廣播接收器可以自由地控制注冊與注銷,有很大的靈活性,但有一個缺點,必須要在程序啟動之后才能接收廣播,因為注冊的邏輯是寫在onCreate()方法中的,可以采取靜態注冊的方式,讓程序在未啟動的情況下接收到廣播;
我們讓程序接收一條開機廣播,當收到這條廣播時就可以在onReceive()方法里執行相應的邏輯,從而實現開機啟動的功能。我們右擊com.example.broadcasttest包,-->New->another-->Broadcast Receiver,將這個廣播接收器命名為BootCompleteReceiver,Exported屬性表示是否允許廣播接收器接收本程序以外的廣播,Enabled屬性表示是否啟用這個廣播接收器。勾選這兩個屬性,點擊Finish完成創建。
然后修改BootCompleteReceiver中的代碼,如下:
package com.example.broadcasttest;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;public class BootCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// TODO: This method is called when the BroadcastReceiver is receiving// an Intent broadcast.Toast.makeText(context,"Boot Complete",Toast.LENGTH_SHORT).show();}
}
代碼很簡單,只是在onReceiver()方法中使用Toast彈出一段提示信息。
另外靜態的廣播接收器一定要在AndroidManifest.xml文件中注冊才可以使用,不過由于我們使用的是Android Studio的快捷方式創建的廣播接收器,因此注冊這一步已經被自動完成了。代開AndroidManifest.xml文件看一看,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.broadcasttest"><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><receiverandroid:name=".BootCompleteReceiver"android:enabled="true"android:exported="true"></receiver></application></manifest>
可以看到在,<application>標簽內出現了一個新的標簽<receiver>,所有靜態的廣播接收器都是在這里進行注冊的。她的用法其實和<activity>標簽非常相似,也是通過android:name來指定注冊具體哪一個廣播接收器,而enabled和exported屬性則是根據我們剛才勾選的狀態自動生成的。
不過目前BootCompleteReceiver還是不能接收到開機廣播的,我們還需要對AndroidManifest.xml文件進行修改才行,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.broadcasttest"><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name = "android.permision.RECEIVE_BOOT_COMPLETED"/><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><receiverandroid:name=".BootCompleteReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver></application></manifest>
由于Android系統啟動完成后會發出一條值為android.intent.action.BOOT_COMPLETED的廣播,因此我們在<intent-filter>標簽里添加了相應的action。另外監聽系統開機廣播也是需要權限的,可以看到,我們使用<uses-permission>標簽又加入了一條android.permission.RECEIVE_BOOT_COMPLETED權限。
我們重新運行程序之后,就可以接收開機廣播了。
我們在廣播接收器中的onReceive()方法都只是簡單的使用Toast提示了一段文本信息,當你真正在項目中使用到它的時候,就可以在里面編寫自己的邏輯。不要在onReceive()方法中添加過多的邏輯或者進行任何的耗時操作,因為在廣播接收器中式不允許開啟線程的,當onReceive()方法運行了較長時間還沒有結束時,程序就會報錯。因此廣播接收器更多的扮演一種打開程序或其他組件的角色,比如創建一條狀態欄通知,或者啟動一個服務等。