淺談android Service和BroadCastReceiver

1.題記

       Android中的服務和windows中的服務是類似的東西,服務一般沒有用戶操作界面,它運行於系統中不容易被用戶發覺,可以使用它開發如監控之類的程序。

       廣播接收者(BroadcastReceiver)用於接收廣播Intent,廣播Intent的發送是通過調用Context.sendBroadcast()、Context.sendOrderedBroadcast()來實現的。通常一個廣播Intent可以被訂閱了此Intent的多個廣播接收者所接收,這個特性跟JMS中的Topic消息接收者類似。

2.Service開發詳解

服務的開發比較簡單,如下: 
第一步:繼承Service類 
public class SMSService extends Service { } 

第二步:在AndroidManifest.xml文件中的<application>節點裏對服務進行配置: 
<service android:name=".SMSService" /> 

服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啓動服務。這兩個方法都可以啓動Service,但是它們的使用場合有所不同。使用startService()方法啓用服務,訪問者與服務之間沒有關連,即使訪問者退出了,服務仍然運行。使用bindService()方法啓用服務,訪問者與服務綁定在了一起,訪問者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。 

採用Context.startService()方法啓動服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。

startService 與 bindService區別如下:

  1. 生命週期:startService方式啓動,Service是通過接受Intent並且會經歷onCreate和onStart。當用戶在發出意圖使之銷燬時會經歷onDestory而bindService方式啓動,與Activity綁定的時候,會經歷onCreate和onBind,而當Activity被銷燬的時候,Service會先調用onUnbind然後是onDestory.
  2. 控制方式:牽着的控制方式需要使用固定的方法,對Service進行單一的操作。而後者由於與Activity綁定,不用考慮其生命週期問題,並且從發送Intent的被動操作,變爲可以主動對Service對象進行操作,我們深圳可以建立一個Handler類,對Service進行相關的操作。大大加強了Service的靈活性、可操作性。
  3. 總結:對於簡單的應用startService啓動方式能帶來更少的代碼,簡單的操作,對於複雜的應用bindService方式,雖然帶來的更多的編碼,但同時也帶來了更好的可操作性,使其實用起來更像Activity。

3.BroadcastReceiver開發詳解

3.1BroadcastReceiver廣播接收者

要實現一個廣播接收者方法如下: 
第一步:繼承BroadcastReceiver,並重寫onReceive()方法。 
public class IncomingSMSReceiver extends BroadcastReceiver { 
    @Override public void onReceive(Context context, Intent intent) { 
    } 

第二步:訂閱感興趣的廣播Intent,訂閱方法有兩種: 
第一種:使用代碼進行訂閱

Java代碼  收藏代碼
  1. IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");   
  2. IncomingSMSReceiver receiver = new IncomingSMSReceiver();   
  3. registerReceiver(receiver, filter);   

 
第二種:在AndroidManifest.xml文件中的<application>節點裏進行訂閱:

Xml代碼  收藏代碼
  1. <receiver android:name=".IncomingSMSReceiver">   
  2.     <intent-filter>   
  3.          <action android:name="android.provider.Telephony.SMS_RECEIVED"/>   
  4.     </intent-filter>   
  5. </receiver>   

 

           廣播被分爲兩種不同的類型:“普通廣播(Normal broadcasts)”和“有序廣播(Ordered broadcasts)”。普通廣播是完全異步的,可以在同一時刻(邏輯上)被所有接收者接收到,消息傳遞的效率比較高,但缺點是:接收者不能將處理結果傳遞給下一個接收者,並且無法終止廣播Intent的傳播;然而有序廣播是按照接收者聲明的優先級別,被接收者依次接收廣播。如:A的級別高於B,B的級別高於C,那麼,廣播先傳給A,再傳給B,最後傳給C 。優先級別聲明在intent-filter元素的android:priority屬性中,數越大優先級別越高,取值範圍:-1000到1000,優先級別也可以調用IntentFilter對象的setPriority()進行設置 。有序廣播的接收者可以終止廣播Intent的傳播,廣播Intent的傳播一旦終止,後面的接收者就無法接收到廣播。另外,有序廣播的接收者可以將數據傳遞給下一個接收者,如:A得到廣播後,可以往它的結果對象中存入數據,當廣播傳給B時,B可以從A的結果對象中得到A存入的數據。 

Context.sendBroadcast() 
   發送的是普通廣播,所有訂閱者都有機會獲得並進行處理。 

Context.sendOrderedBroadcast() 
   發送的是有序廣播,系統會根據接收者聲明的優先級別按順序逐個執行接收者,前面的接收者有權終止廣播(BroadcastReceiver.abortBroadcast()),如果廣播被前面的接收者終止,後面的接收者就再也無法獲取到廣播。對於有序廣播,前面的接收者可以將數據通過setResultExtras(Bundle)方法存放進結果對象,然後傳給下一個接收者,下一個接收者通過代碼:Bundle bundle = getResultExtras(true))可以獲取上一個接收者存入在結果對象中的數據。 

系統收到短信,發出的廣播屬於有序廣播。如果想阻止用戶收到短信,可以通過設置優先級,讓你們自定義的接收者先獲取到廣播,然後終止廣播,這樣用戶就接收不到短信了。

3.2廣播接收者的響應

在Android中,每次廣播消息到來時都會創建BroadcastReceiver實例並執行onReceive() 方法, onReceive() 方法執行完後,BroadcastReceiver 的實例就會被銷燬。當onReceive() 方法在10秒內沒有執行完畢,Android會認爲該程序無響應。所以在BroadcastReceiver裏不能做一些比較耗時的操作,否側會彈出ANR(Application No Response)的對話框。如果需要完成一項比較耗時的工作,應該通過發送Intent給Service,由Service來完成。這裏不能使用子線程來解決,因爲BroadcastReceiver的生命週期很短,子線程可能還沒有結束BroadcastReceiver就先結束了。BroadcastReceiver一旦結束,此時BroadcastReceiver的所在進程很容易在系統需要內存時被優先殺死,因爲它屬於空進程(沒有任何活動組件的進程)。如果它的宿主進程被殺死,那麼正在工作的子線程也會被殺死。所以採用子線程來解決是不可靠的。 

Java代碼  收藏代碼
  1. public class IncomingSMSReceiver extends BroadcastReceiver {   
  2.     @Override public void onReceive(Context context, Intent intent) {   
  3.             //發送Intent啓動服務,由服務來完成比較耗時的操作   
  4.             Intent service = new Intent(context, XxxService.class);   
  5.             context.startService(service);   
  6.     }   
  7. }   

 
3.3常見系統廣播接收者

除了短信到來廣播Intent,Android還有很多廣播Intent,如:開機啓動、電池電量變化、時間已經改變等廣播Intent。 
接收電池電量變化廣播Intent ,在AndroidManifest.xml文件中的<application>節點裏訂閱此Intent: 
<receiver android:name=".IncomingSMSReceiver"> 
    <intent-filter> 
         <action android:name="android.intent.action.BATTERY_CHANGED"/> 
    </intent-filter> 
</receiver> 

 接收開機啓動廣播Intent在AndroidManifest.xml文件中的<application>節點裏訂閱此Intent: 
<receiver android:name=".IncomingSMSReceiver"> 
    <intent-filter> 
         <action android:name="android.intent.action.BOOT_COMPLETED"/> 
    </intent-filter> 
</receiver> 
並且要進行權限聲明: 
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

接收短信廣播Intent在AndroidManifest.xml文件中的<application>節點裏訂閱此Intent:

 <receiver android:name=".IncomingSMSReceiver"> 
   <intent-filter>

<action android:name="android.provider.Telephony.SMS_RECEIVED"/>

</intent-filter>

</receiver> 
在AndroidManifest.xml文件中添加以下權限: 
<uses-permission android:name="android.permission.RECEIVE_SMS"/><!-- 接收短信權限 --> 
<uses-permission android:name="android.permission.SEND_SMS"/><!-- 發送短信權限 -->

4.簡單實例

      下面是整合了Service與BroadCastReceiver的一個小例子,主要實現的是,在後臺開通一個計數服務,當計數能被5整除時候則廣播該數。主要代碼如下:

    ClientActivity綁定服務:

Java代碼  收藏代碼
  1. public class ClientActivity extends Activity {  
  2.     /** Called when the activity is first created. */  
  3.     private ICountService countService;  
  4.   
  5.     @Override  
  6.     public void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.main);  
  9.         this.bindService(new Intent(this, CountService.class),  
  10.                 this.serviceConnection, BIND_AUTO_CREATE);  
  11.     }  
  12.   
  13.     @Override  
  14.     protected void onDestroy() {  
  15.         super.onDestroy();  
  16.         this.unbindService(serviceConnection);  
  17.     }  
  18.     @Override  
  19.     public boolean onKeyUp(int keyCode, KeyEvent event) {  
  20.         if(keyCode==KeyEvent.KEYCODE_BACK)  
  21.         {  
  22.             this.unbindService(serviceConnection);  
  23.             this.finish();  
  24.             return true;  
  25.         }  
  26.         return super.onKeyUp(keyCode, event);  
  27.     }  
  28.     private ServiceConnection serviceConnection = new ServiceConnection() {  
  29.         @Override  
  30.         public void onServiceConnected(ComponentName name, IBinder service) {  
  31.             countService = (ICountService) service;// 對於本地服務,獲取的實例和服務onBind()返回的實例是同一個  
  32.             int i = countService.getCount();  
  33.             Log.v("CountService""Count is " + i);  
  34.         }  
  35.   
  36.         @Override  
  37.         public void onServiceDisconnected(ComponentName name) {  
  38.             countService = null;  
  39.         }  
  40.     };  
  41.   
  42. }  

   計數服務代碼:

Java代碼  收藏代碼
  1. package com.sulang.android.service;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.Binder;  
  6. import android.os.IBinder;  
  7. import android.util.Log;  
  8.   
  9. /* 
  10.  *@author 七里香的悔恨,2011-3-17 
  11.  *CountService.java 
  12.  *Blog:[url]http://bigboy.iteye.com/[/url] 
  13.  */  
  14. public class CountService extends Service {  
  15.     private boolean quit=false;   
  16.     private int count;   
  17.     private ServiceBinder serviceBinder = new ServiceBinder();   
  18.     private final static String DIVIDE_RESULT="com.sulang.android.service.DIVIDE";  
  19.     @Override  
  20.     public IBinder onBind(Intent intent) {  
  21.         return serviceBinder;   
  22.     }  
  23.     public class ServiceBinder extends Binder implements ICountService {   
  24.         @Override   
  25.         public int getCount() {   
  26.             return count;   
  27.         }   
  28.     }   
  29.     @Override   
  30.     public void onCreate() {   
  31.         super.onCreate();   
  32.         new Thread(new Runnable() {   
  33.             @Override   
  34.             public void run() {   
  35.                 while (!quit) {   
  36.                     try {   
  37.                     Thread.sleep(1000);   
  38.                     } catch (InterruptedException e) {}   
  39.                     count++;   
  40.                     if(count%5==0)  
  41.                     {  
  42.                         Intent intent = new Intent(DIVIDE_RESULT);  
  43.                         intent.putExtra("count", count);  
  44.                         sendBroadcast(intent);  
  45.                     }  
  46.                     Log.i("CountService", count+"");  
  47.                 }   
  48.             }   
  49.         }).start();   
  50.     }   
  51.   
  52.     @Override   
  53.     public void onDestroy() {   
  54.         super.onDestroy();   
  55.         this.quit = true;   
  56.     }   
  57.   
  58. }  

    計數廣播接收者:

Java代碼  收藏代碼
  1. package com.sulang.android.service;  
  2.   
  3. import android.content.BroadcastReceiver;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.widget.Toast;  
  7.   
  8. /* 
  9.  *@author 七里香的悔恨,2011-3-18 
  10.  *CountServiceBroadcast.java 
  11.  *Blog:[url]http://bigboy.iteye.com/[/url] 
  12.  */  
  13. public class CountServiceBroadcast extends BroadcastReceiver {  
  14.     private final static String DIVIDE_RESULT="com.sulang.android.service.DIVIDE";  
  15.     @Override  
  16.     public void onReceive(Context context, Intent intent) {  
  17.         String action = intent.getAction();  
  18.         if(action.equals(DIVIDE_RESULT))  
  19.         {  
  20.             int count = intent.getIntExtra("count"0);  
  21.             Toast.makeText(context, "當前數字爲:"+count, Toast.LENGTH_LONG).show();  
  22.         }  
  23.     }  
  24.     /** 
  25.      * 使用代碼進行訂閱廣播 
  26.      * IntentFilter filter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED"); 
  27.      * IncomingSMSReceiver receiver=new IncomingSMSReceiver(); 
  28.      * registerReceiver(receiver,filter); 
  29.      */  
  30.   
  31. }  

   manifest文件:

Xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="com.sulang.android.service"  
  4.       android:versionCode="1"  
  5.       android:versionName="1.0">  
  6.     <uses-sdk android:minSdkVersion="4" />  
  7.   
  8.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  9.         <activity android:name=".ClientActivity"  
  10.                   android:label="@string/app_name">  
  11.             <intent-filter>  
  12.                 <action android:name="android.intent.action.MAIN" />  
  13.                 <category android:name="android.intent.category.LAUNCHER" />  
  14.             </intent-filter>  
  15.         </activity>  
  16.         <service android:name=".CountService">  
  17.             <intent-filter>  
  18.                 <action android:name="com.sulang.android.service.Count"></action>  
  19.                 <category android:name="android.intent.category.DEFAULT"></category>  
  20.             </intent-filter>  
  21.         </service>  
  22.         <receiver android:name=".CountServiceBroadcast">  
  23.             <intent-filter>  
  24.                 <action android:name="com.sulang.android.service.DIVIDE" />  
  25.                 <category android:name="android.intent.category.DEFAULT"></category>  
  26.             </intent-filter>  
  27.         </receiver>  
  28.     </application>  
  29. </manifest>  

    具體效果圖如下:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章