本文介紹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 {//如果接收的事件發生 @Overridepublic 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結合使用以確保執行這個操作的進程在整個執行過程中都保持激活狀態。