Broadcast Receiver——廣播接受者,用來監聽系統或者開發者開發的事件做出響應,比如說開機、電池電量的變化、網絡狀況的變化,客戶交互中發出的事件廣播,可以通過廣播接收並通知用戶。
一、廣播的功能與特徵
1、廣播的生命週期很短,從context.sendBroadcast(Intent intent),Broadcast Receiver.onReceiver方法,廣播就結束生命週期。
2、與其他四大組件一樣,廣播也是在同一進程的主線程中完成,是同步並快速的,不推薦在廣播中新開子線程,因爲往往子線程還未完成任務,廣播已經被銷燬,如果要進行耗時操作,最好通過intent啓動Service,在服務中進行耗時操作。
3、如果onReceiver方法10秒內沒有執行完畢任務,Android認爲該程序沒反應。
二、廣播監聽的兩種方法
廣播監聽有靜態註冊和動態註冊(在實際開發中,動態註冊常用一些,靜態註冊也必不可少)。
靜態註冊,將廣播接收器的配置信息寫在應用的配置文件中(AndroidManifest.xml),當有廣播發生時,組件管理服務會從應用安裝包管理服務中獲取已經安裝的廣播組件信息。
動態註冊,Context.register,動態將廣播接收器與所需監聽的事件綁定起來。
1.靜態註冊——跟其他組件一樣,寫在AndroidManifest.xml文件中Application標籤之內
<receiver android:name=".ColdReceiver"><!-- 你的Receiver名稱 -->
<intent-filter>
<action android:name="android.intent.action.COLD_BROADCAST"/> <!-- 你廣播要接受的intent名稱 -->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
然後我們創建一個ColdReceiver類,繼承BroadcastRecevier類
public class ColdReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//跳轉到service中
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(1);
//開啓service
context.startService(intent);
//日誌打印
Log.d("TEST","靜態註冊");
}
}
上面的Service啓動看到了嗎,這裏我用了上次說過的快捷跳轉。其中service的配置和內容如下
<service android:name=".BroadcastService"><!-- 你自定義的service文件 (在<application></application>裏面加)-->
<intent-filter>
<action android:name="android.intent.action.BroadcastService" /><!-- 用intent啓動時的快捷名(也可以用常規的方式啓動) -->
<category android:name="android.intent.category.default" />
</intent-filter>
</service>
public class BroadcastService extends Service{
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
//開啓服務時會首先調用該方法
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//根據每次intent傳過來的信息進行判斷來顯示不同信息
switch(intent.getFlags()){
case 1:{
Toast.makeText(getApplicationContext(), "靜態註冊", Toast.LENGTH_SHORT).show();
break;
}
case 2:{
Toast.makeText(getApplicationContext(), "動態註冊", Toast.LENGTH_SHORT).show();
break;
}
case 3:{
Toast.makeText(getApplicationContext(), "普通廣播", Toast.LENGTH_SHORT).show();
break;
}
case 4:{
Toast.makeText(getApplicationContext(), "有序廣播", Toast.LENGTH_SHORT).show();
break;
}
}
return START_STICKY;
}
@Override
public void onDestroy() {
// 停止service後會調用此方法
Log.d("TEST", "destroy");
super.onDestroy();
}
}
那麼靜態廣播的創建就完成了,簡單吧,就兩個步驟,一是配置廣播,二是繼承BroadcastReceiver,重寫裏面的onReceive函數。接下來我們在新建一個工程來檢測廣播是否可以響應消息。public class MainActivity extends Activity implements OnClickListener{
private Button b1,b2,b3,b4;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent();
//獲得界面的控件
b1 = (Button) findViewById(R.id.button1);
b1.setOnClickListener(this);
b2 = (Button) findViewById(R.id.button2);
b2.setOnClickListener(this);
b3 = (Button) findViewById(R.id.button3);
b3.setOnClickListener(this);
b4 = (Button) findViewById(R.id.button4);
b4.setOnClickListener(this);
Log.d("TEST","===初始化完成===");
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.button1:{//發送到靜態註冊廣播
intent = new Intent("android.intent.action.COLD_BROADCAST");
sendBroadcast(intent);
//intent.putExtra("msg", "hello coldreceiver.");
break;
}
case R.id.button2:{//發送到動態註冊廣播
intent = new Intent("android.intent.action.HOT_BROADCAST");
//intent.putExtra("msg", "hello hotreceiver.");
sendBroadcast(intent);
break;
}
case R.id.button3:{//普通廣播
intent = new Intent("android.intent.action.NORMAL_BROADCAST");
sendBroadcast(intent);
break;
}
case R.id.button4:{//有序廣播
intent = new Intent("android.intent.action.SORT_BROADCAST");
sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION");
break;
}
}
}
public void show(String str){
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
}
ok,將兩個應用都安裝到設備上,啓動測試用的應用,點擊第一個按鈕,運行的效果如下
同時,會出現"靜態註冊"的Toast,可以看出靜態註冊廣播能夠跨應用來響應信息,這都要歸功於安卓上的組件管理服務,它會讀取每個應用的配置文件,然後獲取裏面的組件信息,每當有消息響應時,組件管理服務會從中查找有沒有需要調用的組件,並判斷是否進行執行
2、動態註冊
動態註冊也可以分成兩部分,一在代碼中進行動態註冊,二還是繼承BroadcastReceiver,重寫裏面的onReceive函數。我們在廣播應用中新建一個HotReceiver,繼承BroadcastReceiver。
public class HotReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//String msg = intent.getStringExtra("msg");
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(2);
context.startService(intent);
Log.d("TEST","動態註冊");
}
}
在Activity中進行動態註冊
public class MainActivity extends Activity {
private HotReceiver receiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//動態註冊廣播
//新建一個廣播對象
receiver = new HotReceiver();
//新建一個intent管理機制,(功能是對組件進行過濾,只獲取需要的消息)
IntentFilter filter = new IntentFilter();
//添加白名單(只獲取該動作的信息)
filter.addAction("android.intent.action.HOT_BROADCAST");
//與廣播綁定,進行註冊
registerReceiver(receiver, filter);
}
@Override
protected void onDestroy() {
//取消註冊,一定要記得,不然系統會報錯誤
unregisterReceiver(receiver);
stopService(new Intent("android.intent.action.BroadcastService"));
super.onDestroy();
}
}
ok,再使用測試應用來檢查一下效果,注意步驟,安裝好廣播應用打開,不要讓它退出,切換到測試用的廣播,點擊第二個按鈕。
測試成功。那麼我們關掉廣播應用在測試一下,會發現不會再出現動態註冊的打印消息。這說明動態註冊的廣播是與Activity綁定的,當Activity銷燬時,廣播也會被銷燬。在Android中,很多時候最好是使用動態註冊的方式使用廣播,比如時間變化事件,電量變更事件等,這些事件觸發率太高,如果使用靜態註冊,會導致進程頻繁的被構造和銷燬從而影響整個系統的效率。
三、廣播的兩種類型
1、普通廣播——普通廣播對於多個接收者來說完全是異步的,通常每個接收者無需等待即可接收到廣播,接收者相互之間不影響,對於這種廣播接收者無法阻止廣播,也就是無法阻止其他接收者接受廣播。
新建三個靜態廣播進行測試——調用abortBroadcast阻礙廣播向下一級傳播
public class NormalReceiver1 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(3);
context.startService(intent);
Log.d("TEST","普通廣播1");
abortBroadcast();
}
}
public class NormalReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(3);
context.startService(intent);
Log.d("TEST","普通廣播2");
abortBroadcast();
}
}
public class NormalReceiver3 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(3);
context.startService(intent);
Log.d("TEST","普通廣播3");
abortBroadcast();
}
}
進行配置
<receiver android:name=".NormalReceiver1">
<intent-filter>
<action android:name="android.intent.action.NORMAL_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name=".NormalReceiver2">
<intent-filter>
<action android:name="android.intent.action.NORMAL_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name=".NormalReceiver3">
<intent-filter>
<action android:name="android.intent.action.NORMAL_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
安裝完畢後,我們點擊檢測應用的第三個按鈕,可以看到
經過測試,abortBroadcast在這裏不起作用,並會讓系統報錯。所以如果要讓廣播有一定的優先級進行傳播就要使用到有序廣播。
2、有序廣播
有序廣播通過調用sendOrderedBroadcast函數進行發送。它每次只發送到優先級較高的接收者那裏,然後由優先級高的接受者再傳播到優先級低的接收者那裏,優先級高的接收者有能力終止這個廣播。在有序廣播的傳遞過程中,每個執行中的觸發器組件都可以通過BroadcastReceiver.setResult等函數附加額外的數據,而下一個廣播則可以使用這些數據(BroadcastReceiver.getResultData)。這樣可以構成一個消息數據處理鏈。爲了保證某一事件一定會被處理,可以指明默認的廣播接收器(Final
Receiver)。
一樣的,新建三個靜態廣播樣例
public class SortReceiver1 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//String msg = intent.getStringExtra("msg");
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(4);
context.startService(intent);
Log.d("TEST","有序廣播1");
abortBroadcast();
}
}
public class SortReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//String msg = intent.getStringExtra("msg");
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(4);
context.startService(intent);
Log.d("TEST","有序廣播2");
}
}
public class SortReceiver3 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//String msg = intent.getStringExtra("msg");
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(4);
context.startService(intent);
Log.d("TEST","有序廣播3");
}
}
<receiver android:name=".SortReceiver1">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.SORT_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name=".SortReceiver2">
<intent-filter android:priority="999">
<action android:name="android.intent.action.SORT_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name=".SortReceiver3">
<intent-filter android:priority="998">
<action android:name="android.intent.action.SORT_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
我們看到,現在這三個接收者的<intent-filter>多了一個android:priority屬性,並且依次減小。這個屬性的範圍在-1000到1000,數值越大,優先級越高。
同樣發送廣播的代碼也是不一樣的
sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION");
注意,使用sendOrderedBroadcast方法發送有序廣播時,需要一個權限參數,如果爲null則表示不要求接收者聲明指定的權限,如果不爲 null,則表示接收者若要接收此廣播,需聲明指定權限。這樣做是從安全角度考慮的,例如系統的短信就是有序廣播的形式,一個應用可能是具有攔截垃圾短信 的功能,當短信到來時它可以先接受到短信廣播,必要時終止廣播傳遞,這樣的軟件就必須聲明接收短信的權限。
所以我們在AndroidMainfest.xml中定義一個權限,並獲得權限:(是要在廣播的應用中聲明)
<permission android:protectionLevel="normal"
android:name="scott.permission.SORT_BROADCAST_PERMISSION" />
<uses-permission android:name="scott.permission.SORT_BROADCAST_PERMISSION" />
(這裏不是寫在application內部,而是同application同級)
運行後只會出現這麼一個消息:
四、形形色色的廣播(系統發出的廣播)
在android中有很多系統自帶的intent.action,通過監聽這些事件我們可以完成很多功能。
- 開機:
String BOOT_COMPLETED_ACTION 廣播:在系統啓動後。這個動作被廣播一次(只有一次)。監聽: “android.intent.action.BOOT_COMPLETED” - 電話撥入:
String ANSWER_ACTION 動作:處理撥入的電話。監聽: “android.intent.action.ANSWER” - 電量變化:
String BATTERY_CHANGED_ACTION 廣播:充電狀態,或者電池的電量發生變化。監聽: “android.intent.action.BATTERY_CHANGED” - 日期改變:
String DATE_CHANGED_ACTION 廣播:日期被改變。 監聽:“android.intent.action.DATE_CHANGED” - 取消更新下載:
String FOTA_CANCEL_ACTION 廣播:取消所有被掛起的 (pending) 更新下載。 監聽:“android.server.checkin.FOTA_CANCEL” - 更新開始安裝:
String FOTA_READY_ACTION 廣播:更新已經被下載 可以開始安裝。監聽 “android.server.checkin.FOTA_READY” - 主屏幕:
String HOME_CATEGORY 類別:主屏幕 (activity)。設備啓動後顯示的第一個 activity。 監聽:"android.intent.category.HOME” - 新應用:
String PACKAGE_ADDED_ACTION 廣播:設備上新安裝了一個應用程序包。監聽: “android.intent.action.PACKAGE_ADDED” - 刪除應用:
String PACKAGE_REMOVED_ACTION 廣播:設備上刪除了一個應用程序包。監聽: “android.intent.action.PACKAGE_REMOVED” - 屏幕關閉:
String SCREEN_OFF_ACTION 廣播:屏幕被關閉。監聽: “android.intent.action.SCREEN_OFF” - 屏幕開啓:
String SCREEN_ON_ACTION 廣播:屏幕已經被打開。 監聽:“android.intent.action.SCREEN_ON” - 時區改變:
String TIMEZONE_CHANGED_ACTION 廣播:時區已經改變。監聽: “android.intent.action.TIMEZONE_CHANGED” - 時間改變:
String TIME_CHANGED_ACTION 廣播:時間已經改變(重新設置)。 “android.intent.action.TIME_SET” - 時間流逝:
String TIME_TICK_ACTION 廣播:當前時間已經變化(正常的時間流逝)。 “android.intent.action.TIME_TICK” - 進入大容量存儲模式:
String UMS_CONNECTED_ACTION 廣播:設備進入 USB 大容量存儲模式。 “android.intent.action.UMS_CONNECTED” - 退出大容量存儲模式:
String UMS_DISCONNECTED_ACTION 廣播:設備從 USB 大容量存儲模式退出。 “android.intent.action.UMS_DISCONNECTED” - 壁紙改變:
String WALLPAPER_CHANGED_ACTION 廣播:系統的牆紙已經改變。 “android.intent.action.WALLPAPER_CHANGED” - web搜索:
String WEB_SEARCH_ACTION 動作:執行 web 搜索。 “android.intent.action.WEB_SEARCH” - 網絡變化:
String CONNECTIVITY_CHANGE_ACTION 動作:網絡變化。“android.intent.action.CONNECTIVITY_CHANGE_ACTION”
廣播本身的使用很簡單,它所包含的內容也很少,但是結合廣播來進行應用的開發卻會給用戶帶來更好的體驗,廣播的使用會讓用戶覺得開發者能夠爲用戶考慮到各個情況的發生,這點往往能夠留住大多數的用戶。所以多考慮各種情況的發生,不要吝嗇廣播的使用。