本文介紹Broadcast Receiver,包括幾部分內容:Broadcast Receiver概述及執行個體、自訂Broadcast Receiver、Broadcast Receiver的實現細節、生命週期等。
csdn貌似今天出問題了,無法上傳圖片。
資料來源:最牛網,《官方解讀BroadcastReceiver》《Android中Broadcast Receiver組件詳解》《(轉)第二十一講:Broadcast
Receiver 使用入門》
BroadcastReceiver(廣播接收器)是Android中的四大組件之一。
下面是Android Doc中關於BroadcastReceiver的概述:
①廣播接收器是一個專註於接收廣播通知資訊,並做出對應處理的組件。很多廣播是源自於系統代碼的──比如,通知時區改變、電池電量低、拍攝了一張照片或者使用者改變了語言選項。應用程式也可以進行廣播──比如說,通知其它應用程式一些資料下載完成並處於可用狀態。
②應用程式可以擁有任意數量的廣播接收器以對所有它感興趣的通知資訊予以響應。所有的接收器均繼承自BroadcastReceiver基類。
③廣播接收器沒有使用者介面。然而,它們可以啟動一個activity來響應它們收到的資訊,或者用NotificationManager來通知使用者。通知可以用很多種方式來吸引使用者的注意力──閃動背燈、震動、播放聲音等等。一般來說是在狀態列上放一個持久的表徵圖,使用者可以開啟它並擷取訊息。
Android中的廣播事件有兩種,一種就是系統廣播事件,比如:ACTION_BOOT_COMPLETED(系統啟動完成後觸發),ACTION_TIME_CHANGED(系統時間改變時觸發),ACTION_BATTERY_LOW(電量低時觸發)等等。另外一種是我們自訂的廣播事件。
廣播事件的流程
①註冊廣播事件:註冊方式有兩種,一種是靜態註冊,就是在AndroidManifest.xml檔案中定義,註冊的廣播接收器必須要繼承BroadcastReceiver;另一種是動態註冊,是在程式中使用Context.registerReceiver註冊,註冊的廣播接收器相當於一個匿名類。兩種方式都需要IntentFIlter。
②發送廣播事件:通過Context.sendBroadcast來發送,由Intent來傳遞註冊時用到的Action。
③接收廣播事件:當發送的廣播被接收器監聽到後,會調用它的onReceive()方法,並將包含訊息的Intent對象傳給它。onReceive中代碼的執行時間不要超過5s,否則Android會彈出逾時dialog。
Broadcast Receiver接收系統內建的廣播
我們做一個例子,功能是在系統啟動時播放一首音樂。
1、建立一個項目Lesson21_BroadcastReceiver,拷貝一首音樂進res/raw目錄
2、建立HelloBroadcastReceiver.java 內容如下:
Codepackage android.basic.lesson21;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.media.MediaPlayer;import android.util.Log;public class HelloBroadReciever extends BroadcastReceiver { //如果接收的事件發生 @Override public void onReceive(Context context, Intent intent) { //則輸出日誌 Log.e("HelloBroadReciever", "BOOT_COMPLETED!!!!!!!!!!!!!!!!!!!!!!!!!"); Log.e("HelloBroadReciever", ""+intent.getAction()); //則播放一首音樂 MediaPlayer.create(context, R.raw.babayetu).start(); }}
3、在AndroidManifest.xml中註冊此Receiver :
Code<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionname="1.0" android:versioncode="1" package="android.basic.lesson21"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:label="@string/app_name" android:name=".MainBroadcastReceiver"> <intent -filter=""> <action android:name="android.intent.action.MAIN"> <category android:name="android.intent.category.LAUNCHER"> </category></action></intent> </activity> <!-- 定義Broadcast Receiver 指定監聽的Action --> <receiver android:name="HelloBroadReciever"> <intent -filter=""> <action android:name="android.intent.action.BOOT_COMPLETED"> </action></intent> </receiver></application></manifest>
4、發布程式,啟動模擬器,可以在Logcat中看到:
同時能聽到音樂播放的聲音。說明我們確實接收到了系統啟動的廣播事件,並做出了響應。
三、自訂廣播
下面我們學習自己製作一個廣播。我們接著剛才的例子,繼續寫下去。
5、在MainBroadcastReceiver.java中填寫如下代碼:
Codepackage android.basic.lesson21;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.Button;public class MainBroadcastReceiver extends Activity {/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Button b1 = (Button) findViewById(R.id.Button01);b1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//定義一個intentIntent intent = new Intent().setAction("android.basic.lesson21.Hello").putExtra("yaoyao","yaoyao is 189 days old ,27 weeks -- 2010-08-10");//廣播出去sendBroadcast(intent);}});}}
6、更改 HelloBroadReceiver.java 內容如下:
Codepackage android.basic.lesson21;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.media.MediaPlayer;import android.util.Log;public class HelloBroadReciever extends BroadcastReceiver {//如果接收的事件發生@Overridepublic void onReceive(Context context, Intent intent) {//對比Action決定輸出什麼資訊if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){Log.e("HelloBroadReciever", "BOOT_COMPLETED !!!!!!!!!!!!!!!!!!!!!!!!!");}if(intent.getAction().equals("android.basic.lesson21.Hello")){Log.e("HelloBroadReciever", "Say Hello to Yaoyao !!!!!!!!!!!!!!!!!!!!!!!!!");Log.e("HelloBroadReciever", intent.getStringExtra("yaoyao"));}//播放一首音樂MediaPlayer.create(context, R.raw.babayetu).start();}}
7、更改 AndroidManifest.xml 內容如下:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.basic.lesson21" android:versionname="1.0" android:versioncode="1"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:label="@string/app_name" android:name=".MainBroadcastReceiver"> <intent -filter=""> <action android:name="android.intent.action.MAIN"> <category android:name="android.intent.category.LAUNCHER"> </category></action></intent> </activity> <!-- 定義Broadcast Receiver 指定監聽的Action 這裡我們的接收器,接收了2個Action,一個系統的一個我們自訂的 --> <receiver android:name="HelloBroadReciever"> <intent -filter=""> <action android:name="android.intent.action.BOOT_COMPLETED"> </action></intent> <intent -filter=""> <action android:name="android.basic.lesson21.HelloYaoYao"> </action></intent> </receiver></application><uses -sdk="" android:minsdkversion="8"></uses></manifest>
8、運行程式,點擊按鈕,查看LogCat,聽聽聲音
在使用Broadcast 時我們應該注意到,BroadcastReceiver的子類別都是無狀態的類別,每次收到發送廣播事件後,BroadcastReceiver都會建立一個新的對象,然後再執行onReceive()函數,當onReceive()函數執行完畢後,就立刻刪掉該對象,下一次再收到此廣播後,又會建立一個新的對象。所以說Broadcast組建是Android中最輕薄、最短小的組建。我們增加了一個static的變數numStatic
,和num變數 。代碼如下:
/** * Broadcaster.java * com.androidtest.broadcaster * * Function: TODO * * ver date author * ────────────────────────────────── * 2011-6-9 Leon * * Copyright (c) 2011, TNT All Rights Reserved.*/ package com.androidtest.broadcaster; import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log; /** * ClassName:Broadcaster * Function: TODO ADD FUNCTION * Reason: TODO ADD REASON * * @author Leon * @version * @since Ver 1.1 * @Date 2011-6-9 */public class Broadcaster extends BroadcastReceiver{ private static final String TAG = "Broadcaster";private static int numStatic =100 ;private int num =100 ;@Overridepublic void onReceive(Context context, Intent intent) { // TODO Auto-generated method stubString string = intent.getAction();numStatic= numStatic+50;num=100+50;Log.v(TAG , "The action is "+ string + "Static Number is :" + numStatic + " Object num is :" + num); } }
多次發送廣播,然後輸出的結果如下,我們可以看到static Number 每次執行都會增加,而Object Num因為每次都要建立所以一直都是一個固定的值。
上文中提到了BroadcastReceiver是Android中最輕薄、最短小的組件,它的對象生命週期十分短暫,經過傻蛋測試在BroadcastReceiver中讓線程睡眠10秒(Activity是5秒鐘)的話,Android就會彈出錯誤(和Activity逾時的錯誤相同),同時需要注意的是Activity、Service和BroadcastReceiver都是運行在本進程的主線程裡面的。通過這個測試讓傻蛋進一步產生了疑問,如果在Service中處理一個長時間的任務會怎麼樣?
啟動一個Service,然後在Service的onCreate()方法中添加如下代碼:
try { Log.v(TAG , "sleep start ….."); Thread.sleep(20000); Log.v(TAG,"sleep end …..");} catch (InterruptedException e) { / / TODO Auto-generated catch block e.printStackTrace();}
很簡單就是讓Service睡眠20秒鐘,我們會發現,sleep start…. 和 sleep end….這兩個日誌列印出來了,但是後台還會出現如下錯誤,前台彈出no response逾時對話方塊。
在onCreate()中新啟動一個線程來,睡眠時,程式正常。
所以總結一下:無論是 Activity、BroadcastReceiver還是Service,只要是有長時間處理的任務,就需要重新開一個線程來處理,為什麼會這樣?因為他們都是運行在主線程中的。
在使用BroadcastReceiver時還有一個我們需要注意的:在BroadcastReceiver的onReceive(Context context , Intent intent )這第一個context到底是哪一個context?是Activity還是Application?通過測試發現:
如果你的BroadcastReceiver是通過在Activity中的this.registerReceiver(myBroadcaster, filter); 來註冊的話,那麼這個context就是這個Activity,而如果是通過AndroidManifest來註冊的話,那麼這個context就是:android.app.ReceiverRestrictedContext。
BroadcastReceiver是接收從sendBroadcast()發出的intent的基類。你可以通過Context.registerReceiver()方法在代碼中動態註冊一個BroadcastReceiver的執行個體,也可以通過再AndroidManifest.xml檔案中用<receiver>標籤來靜態聲明。
注意:如果你實在Activity.onResume()方法中註冊的一個receiver,那麼你必須在Activity.onPause()方法中進行登出。(當一個activity處於暫停狀態是不會接收intents的,並且這樣做也可以減小系統不必要的開銷)。不要在Activity.onSaveInstanceState()方法中登出receiver,因為activity從棧中恢複的時候並不會調用這個方法了。
可以接收的broadcast主要分為兩種類型:
普通的broadcasts(通過Context.sendBroadcast發送)是完全非同步。這個broadcast的receiver以無序的狀態運行,經常是在同一時刻運行。這種做法是十分高效的,但是也意味著receiver不能夠利用相互處理的結果或者是調用退出的API來退出(因為不知道哪個receiver先接收到intent)。
有序的broadcasts(通過Context.sendOrderedBroadcast發送)一次只發送給一個receiver。每一個receiver是有序的處理這個intent的,前面的receiver可以傳遞結果給下一個receiver,或者任意一個receiver都可以完全的退出,這樣intent就不會傳遞給其他的receivers.receiver的執行順序可以通過匹配的intent-filter中的android:priority屬性來控制;如果有多個receivers處於同一個優先順序,那麼這幾個receivers將會以任意的順序來執行。
即使是在廣播普通的broadcasts的情況下,系統也有可能在某些情況下轉換為一次發送一個broadcast給一個receriver。特別是當receivers需要建立進程時,在同一時刻僅僅一個receiver可以運行,避免系統因為這些建立的進程而過載。
注意:儘管Intent類是用來發送和接受這些broadcasts,這裡的Intent broadcast機制和那些通過Context.startActivity()方法來啟動activity的intent是完全獨立的。一個BroadcastReceiver是沒辦法觀察和捕獲一個用於啟動activity的intent的;同樣的,當你通過intent來發出broadcast時,你也不可能(通過這個intent)找到或者啟動一個activity的。這兩種操作是完全不同的:通過一個intent來啟動一個activity是一個前台操作,會改變使用者當前互動的對象;而通過intent來發出broadcast是一個後台操作,使用者經常是察覺不到的。
BroadcastReceiver類(通過一個manifest的<receiver>標籤作為一個組件啟動)是應用程式全域聲明周期重要的一部分。
討論的主題
1、Receiver的生命週期
2、許可權
3、進程的生命週期
開發人員指南
更詳細的關於如何擷取和解析一個Intent的內容,請詳見Intents and Intent Filters開發人員指南
Receiver的生命週期
一個BroadcastReceiver的對象僅僅在調用onReceiver(COntext, Intent)的時間中有效。一旦你的代碼從這個函數中返回,那麼系統就認為這個對象應該結束了,不能再被啟用。
你在onReceive(Context, Intent)中的實現有著非常重要的影響:任何對於非同步作業的請求都是不允許的,因為你可能需要從這個函數中返回去處理非同步操作,但是在那種情況下,BroadcastReceiver將不會再被啟用,因此系統就會再非同步作業之前殺死這個進程。
特別是,你不應該再一個BroadcastReceiver中顯示一個對話方塊或者綁定一個服務。對於前者(顯示一個對話方塊),你應該用NotificationManagerAPI來替代,對於後者(綁定一個服務),你可以使用Context.startService()發送一個命令給那個服務來實現綁定效果。
許可權
存取的許可權可以通過在發送方的Intent或者接收方的Intent中強制指定。
在發送一個broadcast時強制指定許可權,就必須提供一個非空的peemission參數給sendBroadcast(Intent, String)或者是sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handel, int, String, Bundle).只有那些擁有這些許可權(通過在ANdroidManifest.xml檔案中相應的聲明<uses-permission>標籤)的receiver能夠接收這些broadcast。
在接收一個broadcast時強制指定許可權,就必須在註冊receiver時提供一個非空的permission參數--無論是在調用registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)或者是通過再AndroidManifest.xml檔案中通過<receiver>靜態標籤來聲明。只有那些擁有這些許可權(通過在相應的AndroidManifest.xml檔案中查詢<uses-permission>標籤來獲知)的發送方將能夠給這個receiver發送Intent。
對於安全和許可權的詳細內容請查看Security and Permission文檔。
進程的生命週期
一個正在執行BroadcastReceiver(也就是,正在執行onReceive(COntext, Intent)方法)的進程被認為是一個前台的進程,將會一直運行,除非系統處於記憶體極度低的情況下。
一旦從OnReceive()方法中返回,這個BroadcastReceiver將不會再被啟用,此時它的主進程就和任何其他運行於此應用程式中的組件擁有相同的優先順序。這一點非常重要,如果進程僅僅只是擁有BroadReceiver(一個普遍的情況是使用者從不或者是最近沒有和它進行互動),因此一旦它從onReceive()方法中返回時,系統就會認為進程是空的並且主動的殺死它,以便這些資源可以被其他重要的進程利用。
這意味著對於耗時的操作,可以採用將Service和BroadcastReceiver結合使用以確保執行這個操作的進程在整個執行過程中都保持啟用狀態。