Android開發之廣播機制
/*
* Android開發之廣播機制
* 北京Android俱樂部群:167839253
* Created on: 2012-7-31
* Author: blueeagle
* Email:liujiaxiang@gmail.com
*/
概述
在Android中,有一些操作完成以後,會發送廣播,比如說發出一條簡訊,或打出一個電話,如果某個程式接收了這個廣播,就會做相應的處理。這個廣播跟我們傳統意義中的電台廣播有些相似之處。之所以叫做廣播,就是因為它只負責“說”而不管你“聽不聽”,也就是不管你接收方如何處理。另外,廣播可以被不只一個應用程式所接收,當然也可能不被任何應用程式所接收。
什麼是Broadcast Receiver
BroadcastReceiver是Android應用程式中的第三個組件。Broadcast Reciver和事件處理機制類似,不同的是事件處理機制是應用程式組件層級的,比如一個按鈕的OnClickListener事件,只能夠在一個應用程式中處理。而廣播事件處理機制是系統層級的,不同的應用程式都可以處理廣播事件。
如何使用Broadcast Receiver
可以通過構建Intent對象,然後調用sendBroadcast()方法將廣播發出。事件的接受是通過一個繼承自BroadcastReceiver的類來實現的。繼承後重寫onReceiver()方法,在該方法中響應事件。當然不能忘記在AndroidManifest.xml檔案中註冊BroadcastReceiver。
一般繼承自BroadcastReceiver的類,是我們根據自己的需求所自訂的。這個自訂的類的作用,就是用來處理android當中所發出的廣播事件。也就是說,Android作業系統中發出的廣播,由BroadcastReceiver這個類來接收。接收到以後,就會調用這個類中的onReceive()方法來進行處理。那麼,在Android系統中,有很多的廣播,如何才能確定,一個receiver負責接收什麼類型的廣播呢?對了。這就是要去書寫AndroidManifest.xml檔案。我們需要在AndroidManifest.xml裡進行註冊,將receiver註冊到應用程式中。這個receiver應該接收什麼類型的事件是由Intent-filter來決定的。
對於onReceive方法,一旦方法返回。系統就會認為對象已經被完成。從而不再是活動狀態。
按照上述說明。簡單的看一個例子,代碼如下:
public class BroadcastReceiverTest extends Activity { private Button myButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); myButton = (Button)findViewById(R.id.myButton); myButton.setOnClickListener(newButton.OnClickListener(){ @Override public void onClick(View v) { Intent myIntent = new Intent(); myIntent.setAction(myIntent.ACTION_EDIT); BroadcastReceiverTest.this.sendBroadcast(myIntent);//發送廣播 } }); }}
需要編寫BroadcastReceiver類,代碼如下:
public class ReceiverTest extends BroadcastReceiver { public ReceiverTest(){ System.out.println("ReceiverTest"); } @Override public void onReceive(Context arg0, Intent arg1) { System.out.println("onReceive"); }}
在AndroidManifest.xml中註冊的代碼如下:
<receiver android:label="@string/app_name" android:name=".ReceiverTest" > <intent-filter > <action android:name="android.intent.action.EDIT" /> </intent-filter> </receiver>
編譯完成之後,我們在控制台會得到的結果:
如果連續按兩次按鈕,則會出現兩次。:
在上面的例子中,Intent的Action類型為ACTION_EDIT,而在AndroidManifest.xml中註冊的receiver類型也是edit,<action
android:name="android.intent.action.EDIT"/>
,這就表明這兩個Action進行了匹配。即執行了sendBroadcast方法之後,類型為edit的receiver就可以進行接收了。類型匹配成功的話,才會產生receiver的對象,從而調用onReceiver方法。上文中已經談到,每次接收廣播都會產生一個新的receiver對象。在處理完畢之後,這個對象就不會再被使用了。
註冊Broadcast Receiver的方法
BroadcastReceiver能夠監聽被廣播出來的對象,一般是會用Intent來進行廣播。那麼,達到能夠監聽的目的,則BroadcastReceiver必須進行註冊。註冊方法有兩種:
在AndroidManifest.xml檔案中進行註冊:
這裡面需要注意一點的是,如果我在AndroidManifest.xml檔案中進行註冊BroadcastReceiver的話,無論這個BroadcastReceiver所在的應用程式是運行狀態還是關閉狀態,這個BroadcastReceiver都是活動的,都可以接收到廣播的事件。例如,簡訊息的接收,電池耗電量的顯示等應用程式。我們在待機狀態時需要監聽這些狀態,但是我們不可能一直開啟應用程式。
在應用程式的代碼中進行註冊:
當我們需要更新Activity裡面的控制項的狀態的時候,則需要在應用程式的代碼中進行註冊,這個時候我們如果在AndroidManifest.xml中註冊的話就不太合適了。因為只有我們在Activity能看到的時候才進行更新,而Activity看不見的時候,這個BroadcastReceiver就應該關閉。否則會浪費各種資源。因此,這個時候就需要在應用程式的代碼中進行註冊。在Activity啟動以後註冊BroadcastReceiver,在Activity不可見後取消註冊。
註冊的代碼很簡單就是registerReceiver(receiver,filter);
相應的,取消註冊的代碼為unregisterReciver(receiver);
參數receiver表示一個BroadcastReceiver對象( TheBroadcastReceiver to handle the broadcast.);filter表示一個Intent-filter對象( Selectsthe Intent broadcasts to be received.),與我們在AndroidManifest.xml 檔案中所使用的Intent-filter標籤的作用是一樣的。後面會討論到如何建立一個Intent-filter對象。
下面用手機接收資訊這個例子來說明。對應代碼如下:
public class BroadcastReceiverTest extends Activity { private Button myRegisterbtn; private Button myUnregisterbtn; private SMSReceiver myMessageReceiver; private static final String SMS_ACTION = "android.provider.Telephony.SMS_RECEIVED"; private static final String EDIT_ACTION = "android.intent.action.EDIT"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); myRegisterbtn = (Button)findViewById(R.id.registerbtn); myUnregisterbtn = (Button)findViewById(R.id.unregisterbtn); myRegisterbtn.setOnClickListener(newmyRegisterListener()); myUnregisterbtn.setOnClickListener(newmyUnregisterListener()); } class myRegisterListener implements OnClickListener{ @Override public void onClick(View v) { myMessageReceiver = new SMSReceiver(); //產生一個BroadReceiver對象 IntentFilter myFilter = new IntentFilter(); //產生一個IntentFilter對象 myFilter.addAction(SMS_ACTION); //為IntentFilter添加一個Action BroadcastReceiverTest.this.registerReceiver(myMessageReceiver, myFilter); } //整個操作完成了BroadcastReceiver的註冊。 } class myUnregisterListener implements OnClickListener{ @Override public void onClick(View v) { BroadcastReceiverTest.this.unregisterReceiver(myMessageReceiver); //將BroadcastReceiver對象在系統中解除註冊 } }}
代碼很簡單,就是定義了兩個按鈕,其中一個是將BroadcastReceiver綁定到應用程式中,另一個則是解除BroadcastReceiver在應用程式中的綁定。相應的,我們還需要將接收器的類編寫出來,為了方便測試,我們只在接收器類裡編寫一個列印輸出,代碼如下:
public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { System.out.println("onReceivestart"); }}
當然,我們這裡是在應用程式中註冊的BroadcastReceiver,所以就不用在AndroidManifest.xml檔案中再去註冊一次了。(讀者可以嘗試著仍然去AndroidManifest.xml檔案中註冊,看看會發送什麼現象。)運行程式,並且點擊註冊按鈕,然後利用DDMS來給模擬器發送一條簡訊。:
當我們點擊完Send按鈕後,查看控制台,則會發現“onReceive start”已經被列印出來了,如所示:
這就意味著,接收器在過濾到發送資訊這樣的事件後,接收到了廣播。至此,BroadcastReceiver的兩種註冊方式就講完了。
講完這兩種方式後,讀者可能會有一系列疑問。那就是,為什麼我這個action是android.provider.Telephony.SMS_RECEIVED的時候,發送一個訊息,對應的接收器就能夠接收到廣播,如果我的action不是這個,那麼發送的廣播還會被接收嗎?又該怎麼樣確定到底一個action對應什麼操作?從而才能夠被廣播接收器接收到。Android系統中又多少內建的這種action?我們能不能自訂一個action?如果可以的話如何定義呢?
首先,我們來考察一下Android內建的Broadcast Actions。
Android內建的Broadcast Actions
在Android平台中,內建了很多Action,用於協助開發人員監聽手機上所發生的各種事件。下面給出幾個內建的BroadcastAction的例子:
android.intent.action.BATTERY_CHANGED
充電狀態,或者電池的電量發生變化
android.intent.action.SCREEN_ON
螢幕已經被開啟
android.intent.action.PACKAGE_REMOVED
裝置上刪除了一個應用程式套件組合
android.intent.action.TIME_TICK
目前時間已經變化(正常的時間流逝)
如果要查詢BroadcastAction的完整列表,可以在官方SDK的協助文檔裡面找到Intent類,在常量定義的解釋中,去找Broadcast Action開頭的資訊就可以了。例如我們找到String類型的常量:ACTION_BATTERY_LOW,後面的解釋是以“Broadcast Action”開頭的。
String ACTION_BATTERY_LOW Broadcast Action: Indicates low batterycondition on the device.
一般來說,Android作業系統給我們提供的內建的Broadcast Action是夠用的,當然我們也可以進行自訂Broadcast的Action。
自訂Broadcast Action
自訂Broadcast Actions其實就是自訂一個字串常量。然後將這個常量廣播出去,接收的地方再接收這個常量就可以了。下面利用代碼來說明自訂BroadcastAction,同時,這裡還將BroadcastReceiver和負責廣播的工程分開成兩個工程來說明,對於不同工程之間的廣播接收的處理方式。代碼如下:
public class MyBroadcast extends Activity { public static final String NEW_BROADCAST = "com.todd.NEW_BROADCAST"; /* 這個靜態常量字串可以自己隨便定義,代表自訂的action*/ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button mybutton =(Button)findViewById(R.id.mybutton); mybutton.setOnClickListener(newButton.OnClickListener() { public void onClick(View v) { Intent myIntent = new Intent(NEW_BROADCAST); myIntent.putExtra("MSG", "地瓜地瓜,我是馬鈴薯!"); myIntent.setAction(NEW_BROADCAST); sendBroadcast(myIntent); } }); }}
XML檔案中只需要定義一個按鈕
<Button android:id="@+id/mybutton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="發送廣播" />
然後再建立一個工程,作為接收的工程,這個工程負責接收廣播工程廣播出來的資料。代碼如下:
public class MyReceiver extends BroadcastReceiver { /** Calledwhen the activity is first created. */ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generatedmethod stub String message = intent.getExtras().getString("MSG"); Toast.makeText(context, message, Toast.LENGTH_LONG).show(); System.out.println(message); }}
到這裡還不算完。需要有一個註冊的過程。註冊的過程,就是讓接收工程知道我要接收來自哪一個Intent的資料。
在Androidmanifest.xml中註冊如下:
<receiver android:name=".MyReceiver"> <intent-filter> <action android:name="com.todd.NEW_BROADCAST" /> </intent-filter> </receiver>
這樣就把"com.todd.NEW_BROADCAST"動作註冊到了接收工程中。如此,接收工程完畢。
但是這樣就結束了嗎?還沒有。如果此時編譯後,廣播工程沒有任何問題,但是接收工程會出錯,classcast的錯誤。這是因為,我們在編譯接收工程的時候,沒有給接收工程一個Activity用於顯示接收的資訊。因此,我們需要定義一個Activity.代碼如下:
public class IntentRes extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }}
當然,在相應的AndroidManifest.xml檔案中要修改,寫為:
<activity android:name=".IntentRes" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
至此,就完成了廣播和接收。他們位於不同的工程中。