Android Service 詳解

一個Service也是一種應用程序組件,它運行在後臺以提供某種服務,通常不具有可見的用戶界面。其它的應用程序組件可以啓動一個Service,即使在用戶切換到另外一個應用程序後,這個Service還是一直會在後臺運行。此外,一個應用程序也可以綁定到一個Service然後使用進程間通信(IPC)方式與Service之間發生交互。例如一個Service可以處理網絡事物,播放音樂,讀寫文件或者讀寫ContentProvider,所以這些都在後臺運行。

   一個Service可以以以下兩種形式存在:

·        Started(啓動) 在一個應用程序以startService() 來啓動一個Service時,這個Service將處於“Started”狀態。一旦啓動,這個Service可以在後臺一直運行下去,即使啓動它的應用程序已推出。通常,一個處於“started”狀態的Service完成某個功能而不會給啓動它的應用程序組件返回結果。比如,這個服務(Service)可能是上載或是下載一個文件,當任務完成後,服務自行退出。

·        Bound (綁定) 當一個應用程序組件以bindService() 綁定一個Service時,這個Service處於“Bound”狀態。處於“Bound”狀態的Service提供了一個客戶/服務(C/S)調用接口支持其它應用程序組件和它交互,比如發生請求,返回結果,或者使用IPC完成跨進程間通信。一個處於“Bound”的Service只能和與其綁定的應用程序一起運行。多個應用程序組件可以綁定到同一個Service。當所有綁定的應用程序組件都退出綁定後,被“綁定”的Service將被銷燬。

  對於一個Service來說,它可以是“Started”,“Bound”或者同時處於兩種狀態。其它任一應用程序組件(比如一個Activity)都可以使用這個Service,即使其它應用程序組件是在不同的應用程序中。當然你可以把Service定義爲私有的,這樣其它應用程序就無法使用你定義的Service。

  要注意的是,一個Service運行在其宿主進程的主線程中--服務不會自己創建新的線程也不運行在獨立的進程中(除非你特別指明)。這意味着,如果你的Service需要完成一些很耗CPU資源的操作(比如播放MP3,或者使用網絡),你應該在Service中創建新的線程來完成這些工作,從而降低出現程序無響應(ANR)的風險。

Service基礎

   要創建一個Service,你必須從Service或是其某個子類派生子類。在你的Service子類實現中,你需要重載一些方法以支持Service重要的幾個生命週期函數和支持其它應用組件綁定的方法。下面給出幾個需要重載的重要方法:

·        onStartCommand()   Android系統在有其它應用程序組件使用startService()請求啓動Service時調用。一旦這個方法被調用,Service處於“Started”狀態並可以一直運行下去。如果你實現了這個方法,你需要在Service任務完成時調用stopSelf()或是stopService()來終止服務。如果你只支持“綁定”模式的服務,你可以不實現這個方法。

·        onBind()  Android系統中有其他應用程序組件使用bindService()來綁定你的服務時調用。在你實現這個方法時,你需要提供一個IBinder接口以支持客戶端和服務之間通信。你必須實現這個方法,如果你不打算支持“綁定”,返回Null即可。

·        onCreate()  Android系統中創建Service實例時調用,一般在這裏初始化一些只需單次設置的過程(在onStartCommand和onBind()之前調用),如果你的Service已在運行狀態,這個方法不會被調用。

·        onDestroy() Android系統中Service不再需要,需要銷燬前調用。在你的實現中你需要釋放一些諸如線程,註冊過的listener,receiver等,這是Service被調用的最後一個方法。

如果一個Service是由startService()啓動的(這時 onStartCommand()將被調用),這個Service將一直運行直到調用stopSelf()或其它應用部件調用stopService()爲止。

如果一個部件調用bindService()創建一個Service(此時onStartCommand()不會調用),這個Service運行的時間和綁定它的組件一樣長。一旦其他組件解除綁定,系統將銷燬這個Service。

Android系統只會在系統內存過低且不得不爲當前活動的Activity恢復系統資源時纔可能強制終止某個Service。如果這個Service綁定到一個活動的Activity,基本上不會被強制清除。如果一個Service被申明成“後臺運行”,就幾乎沒有被銷燬的可能。否則的話,如果Service啓動後並長期運行,系統將隨着時間的增加降低其在後臺任務中的優先級,其被殺死的可能性越大。如過你的Service是做爲“Started”狀態運行,你必須設計後如果在系統重啓服務時優雅退出。如果系統殺死你的服務,系統將在系統資源恢復正常時重啓你的服務(當然這也取決於onStartCommand()的返回值)。

在Manifest中申明Service

    和Activity一樣,你必須在Manifest文件中申明應用中使用到的Service。爲了聲明一個Service,你需要定義<application>的子元素<service>,比如:

  1. <manifest ... >  
  2.   ...  
  3.   <application ... >  
  4.       <service android:name=".ExampleService" />  
  5.       ...  
  6.   </application>  
  7. </manifest>  


        <service>還包括一些其它屬性,android:name是唯一一個必須定義的屬性,它定義了Service的類名。

和Activity一樣,你可以爲Service定義Intent Filter以支持其它部件隱式調用該服務。如果你只打算在你自己的應用中使用某個Service,那麼就不要定義任何Intent Filter。在這種情況下,你必須明確指明Service的類名來啓動這個Service。此外,你可以通過設置android:exported屬性爲false來明確說明該Service爲私有,即使你爲Service定義了Intent Filter ,其它應用也無法使用你的Service。

 創建一個“Started”的Service

當其它應用程序組件使用startService()來啓動Service後,這個Service就成爲“Started”的Service。這時Service的onStartCommand () 回調函數將會被調用。

當一個Service以startService()啓動後,處於“Started”狀態,其生命週期獨立於啓動這個Service的其它應用程序組件,並且可以在後臺無限期運行,即使啓動它的其它應用程序組件已經退出。在這種情況下,Service可以調用stopSelf()或者其它程序部分調用stopService()來結束這個Service。

一個應用程序組件比如一個Activity可以通過傳入Intent使用startService()來啓動一個Service,這個Intent指明需要的服務幷包含了Service需要的數據。Service在onStartCommand回調函數中可以訪問這個Intent。

要注意的是,Service缺省和申明它的應用程序使用同一個進程並且運行在主線程中,因此如果你的Service比較耗時的話,那麼這個Service會影響到應用的用戶響應性能。爲避免這種現象,你應用在Service中創建新線程。

你可以使用下面兩個基類來創建一個“Started”的Service。

·        Service 這是所有Service的基類,當你派生這個類時,使用新創建的線程來完成Service需要完成的工作非常重要,這是因爲Service缺省也會使用你的應用程序的主線程,這可能會影響你應用程序的響應性能。

·        IntentService 爲Service的一個子類,它會使用一個工作線程來處理所有的請求,每次處理一個請求。這在你無需實現並行處理多個請求時是最好的選擇。你只需重載onHandleIntent()方法,這個方法接受每個請求,從而你可以在後臺完成所需任務。

 派生IntentService類

因爲大部分“Started”的Service不需要並行處理多個請求,這時最好的選擇是派生自IntentService。

類IntentService可以完成以下工作:

·        創建一個工作線程來處理所有發送到onStartCommand()的請求(Intent),從而和應用的主線程分開。

·        創建一個工作隊列來逐個處理每個請求,從而無需考慮多線程編程。

·        在處理完所有請求後中止Service的運行,你無需調用stopSelf()來終止Service。

·        提供onBind()的缺省實現,返回null。

·        提供onStartCommand()的缺省實現,將接受到的Intent發送到工作隊列中,然後調用你的onHanleIntent()實現。

因此,對於你來說,只需要提供onHandleIntent()來處理發過來的Intent,當然你可能需要實現Service的一個簡單的構造函數。

下例爲IntentService的一個簡單實現:

  1. public class HelloIntentService extends IntentService {  
  2.   
  3.     /** 
  4.      * A constructor is required, and must  
  5.      * call the super IntentService(String) 
  6.      * constructor with a name for the worker thread. 
  7.      */  
  8.     public HelloIntentService() {  
  9.         super("HelloIntentService");  
  10.     }  
  11.   
  12.     /** 
  13.      * The IntentService calls this method from  
  14.      * the default worker thread with 
  15.      * the intent that started the service.  
  16.      * When this method returns, 
  17.      * IntentService stops the service, as appropriate. 
  18.      */  
  19.     @Override  
  20.     protected void onHandleIntent(Intent intent) {  
  21.         // Normally we would do some work here, like download a file.  
  22.         // For our sample, we just sleep for 5 seconds.  
  23.         long endTime = System.currentTimeMillis() + 5 * 1000;  
  24.         while (System.currentTimeMillis() < endTime) {  
  25.             synchronized (this) {  
  26.                 try {  
  27.                     wait(endTime - System.currentTimeMillis());  
  28.                 } catch (Exception e) {  
  29.                 }  
  30.             }  
  31.         }  
  32.     }  


你需要實現的只是一個構造函數和重載onHandleIntent()。

如果你需要重載其它一些回調函數,比如onCreate(),onStartCommand()或是onDestroy(),注意調用其基類的實現。這樣IntentService才能正確管理工作線程的生命週期。

比如重載onStartCommand(),你必須返回基類的onStartCommand()的返回值,這樣Intent能夠傳遞到onHandleIntent()的原因:

  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     Toast.makeText(this"service starting", Toast.LENGTH_SHORT).show();  
  4.     return super.onStartCommand(intent,flags,startId);  
  5. }  


   除了onHandleIntent(),此外唯一可以不調用基類實現的回調函數爲onBind(),你只要在你需要支持綁定服務時才需要實現該方法。

派生Service類

   從上面可以看到使用IntentService可以讓實現“Started”的Service變的非常簡單,然而如果你需要支持使用多線程並行處理多個請求(而非使用工作隊列來依次處理請求),這時你就需要從Service類來派生。

   作爲比較,下面的例子使用Service實現和上面使用IntentService同樣的功能,也是使用一個工作線程來逐個處理每個請求,可以看成是IntentService的一個實現:

  1. public class HelloService extends Service {  
  2.   private Looper mServiceLooper;  
  3.   private ServiceHandler mServiceHandler;  
  4.   
  5.   // Handler that receives messages from the thread  
  6.   private final class ServiceHandler extends Handler {  
  7.       public ServiceHandler(Looper looper) {  
  8.           super(looper);  
  9.       }  
  10.       @Override  
  11.       public void handleMessage(Message msg) {  
  12.           // Normally we would do some work here, like download a file.  
  13.           // For our sample, we just sleep for 5 seconds.  
  14.           long endTime = System.currentTimeMillis() + 5*1000;  
  15.           while (System.currentTimeMillis() < endTime) {  
  16.               synchronized (this) {  
  17.                   try {  
  18.                       wait(endTime - System.currentTimeMillis());  
  19.                   } catch (Exception e) {  
  20.                   }  
  21.               }  
  22.           }  
  23.           // Stop the service using the startId, so that we don't stop  
  24.           // the service in the middle of handling another job  
  25.           stopSelf(msg.arg1);  
  26.       }  
  27.   }  
  28.   
  29.   @Override  
  30.   public void onCreate() {  
  31.     // Start up the thread running the service.  Note that we create a  
  32.     // separate thread because the service normally runs in the process's  
  33.     // main thread, which we don't want to block.  We also make it  
  34.     // background priority so CPU-intensive work will not disrupt our UI.  
  35.     HandlerThread thread = new HandlerThread("ServiceStartArguments",  
  36.             Process.THREAD_PRIORITY_BACKGROUND);  
  37.     thread.start();  
  38.       
  39.     // Get the HandlerThread's Looper and use it for our Handler   
  40.     mServiceLooper = thread.getLooper();  
  41.     mServiceHandler = new ServiceHandler(mServiceLooper);  
  42.   }  
  43.   
  44.   @Override  
  45.   public int onStartCommand(Intent intent, int flags, int startId) {  
  46.       Toast.makeText(this"service starting", Toast.LENGTH_SHORT).show();  
  47.   
  48.       // For each start request, send a message to start a job and deliver the  
  49.       // start ID so we know which request we're stopping when we finish the job  
  50.       Message msg = mServiceHandler.obtainMessage();  
  51.       msg.arg1 = startId;  
  52.       mServiceHandler.sendMessage(msg);  
  53.         
  54.       // If we get killed, after returning from here, restart  
  55.       return START_STICKY;  
  56.   }  
  57.   
  58.   @Override  
  59.   public IBinder onBind(Intent intent) {  
  60.       // We don't provide binding, so return null  
  61.       return null;  
  62.   }  
  63.     
  64.   @Override  
  65.   public void onDestroy() {  
  66.     Toast.makeText(this"service done", Toast.LENGTH_SHORT).show();   
  67.   }  
  68. }  


    可以看出來,比使用IntentService多了很多工作。然而,由於你自己來處理onStartCommand(),你可以實現並行處理多個請求。當然這不是本例的實現,但是如果你願意,你可以爲每個請求創建一個工作線程來立即處理請求而不使用工作隊列。

注意onStartCommand()方法必須返回一個整數,這個返回值定義瞭如果Android系統在殺死這個Service後又需要使用這個Service後的行爲。這個返回值可以有下面幾種情況:

·        START_NOT_STICKY 如果Android系統在onStartCommand() 返回後殺死了這個Service,系統不會重新創建這個Service,除非它是一個Pending Intent。這是在你的應支持簡單的重啓沒有完成的任務避免無必要的Service運行時ic最好的選項。

·        START_STICKY 在Android系統在onStartCommand () 返回後,選擇重新創建這個Service並調用Service的onStartCommand () 方法,但不會重新發送上一次的請求。Android系統使用空Intent調用onStartCommand,除非它是一個PendingIntent。這種情況適用於一些媒體播放器服務。

·        START_REDELIVER_INTENT在Android系統在onStartCommand() 返回後,選擇重新創建這個Service並調用Service的onStartCommand () 方法,並且重新發送上一次的請求。這適用一些比較活躍地進行某些工作的服務,需要立即恢復活動比如下載文件的服務。

 啓動Service

   在Activity或是其他應用程序組件可以通過傳遞Intent調用startService()方法來啓動一個Service。Android系統然後會調用Service的onStartCommand()方法並傳入這個Intent對象。

   比如,一個Activity可以通過明確指明調用的服務來調用前面創建的HelloService:

  1. Intent intent = new Intent(this, HelloService.class);  
  2. startService(intent);  


    startService() 調用後立即返回,Android系統調用Service的onStartCommand(),如果這個Service之前沒有運行,系統還將先調用Service的onCreate()方法,然後再調用onStartCommand()。

   如果Service沒有提供綁定支持,那麼使用startService()是唯一可以啓動Service的方法。然而,如果你需要Service返回一個結果,那麼客戶端可以通過創建一個用於廣播的延遲Intent(PendingIntent)然後通過Intent發送請求到Service。Service可以利用這個廣播(Broadcast)返回結果。

   多個啓動Service請求將導致多次調用Service的onStartCommand()方法。然而只要一個stopSelf或stopService來停止一個Service的運行。

 停止Service

一個“Started”的Service必須管理自己的生命週期,也就是說,除非系統資源不足,Android系統不終止或停止一個Service。因此Service必須調用stopSelf () 方法來終止自身的運行,或者其它部件可以通過stopService () 方法來中止一個Service的運行。

一旦Service被請求終止,系統將盡快銷燬這個Service。

但是,如果你的Service需要在onStartCommand()並行處理多個請求,那麼你不應再處理完一個請求後就停止Service的運行,因爲你可能還會接受到新的請求(在第一個請求處理完就結束則會終止第二個請求的執行)。爲避免這種情況,你可以使用帶參數的stopSelf(int) 來確保終止Service的請求總是對應於最近的請求。也就是說,在調用stopSelf(int) 時,你通過傳入請求的Id (傳給onStartCommand 方法的 startId )來終止對應的請求處理。那麼如果Service在調用stopSelf (int) 前接受到一個新的請求,那麼Id將不會匹配,因此Service也就不會結束。

創建一個支持“綁定”的Service

一個支持“綁定”的Service是指運行其它應用程序之間通過調用bindService() 來綁定到它的Service。支持“綁定”的Service在設計時通常不允許直接使用startService() 來啓動它。

在Activity或其它組件需要和一個Service發生交互時,你應該創建一個“綁定”的Service,在這個Service中你可以通過進程間通信接口(IPC)來提供可供其它應用程序組件使用的功能接口。

爲了創建一個支持“綁定”的Service,你需要實現onBind() 回調函數並返回一個IBind接口對象,這個對象提供了其它部分可以訪問這個Service的服務接口。其它應用程序組件然後可以通過bindService () 方法獲得Service的服務接口對象,然後使用這個對象的方法來調用服務。通常“綁定”的Service的運行週期和綁定它的其它應用程序組件的生命週期是一樣的。因此如果這個Service不再有應用程序組件與之綁定,Android系統會自動銷燬這個Service,而你自己無需停止這個Service。

爲了創建一個支持“綁定”的Service所需做的第一件事是定義可供客戶端調用的服務接口。這個接口必須爲一個IBinder接口,並且你的Service必須通過onBind () 返回這個接口對象。一旦客戶端獲得這個IBinder接口,就可以通過這個接口對象來使用服務。

多個客戶端可以同時綁定到同一個Service,當一個客服端使用好所需的服務後,調用onbindService() 來解除與Service之間的綁定關係。當一個Service沒有和它綁定的客戶端後,Android系統銷燬這個Service。

給用戶發送通知

   Service通常是在後臺運行,它可以通過Toast 通知或是狀態條通知來提醒用戶發生了某種事件。

   一個Toast通知爲覆蓋在當前屏幕上短時間顯示的消息,而狀態條通知可以在狀態條以圖標和文字的方法顯示,用戶可以選擇是否查閱這個通知並作出處理(比如啓動一個Activity)。

   一般來說,使用狀態條通知是在某個後臺工作完成後(比如文件下載完畢)通知用戶的最好方法。用戶可以在擴展後的通知窗口選擇某個通知,然後可以點擊是否啓動一個Activity(比如查看下載好的文件)

在前臺運行Service

通常Service在後臺運行,但有時某些Service是用戶明確知道在運行,即使在系統資源不足時,也不該做爲銷燬的候選Service,這種Service可以設置成在前臺運行,在前臺運行的Service必須在狀態條顯示通知,除非Service停止或者轉入後臺,這種通知不可以被忽視。

比如,如果一個音樂播放器使用Service來播放,這個Service應該被放在前臺運行,因爲用戶很清楚的知道音樂在播放,而在狀態條顯示的通知理當顯示當前播放的樂曲並且允許用戶啓動一個Activity來操作這個音樂播放器。

要把一個Service放在前臺運行,可以調用startForeground()。這個方法接受兩個參數:一個整數來唯一標識一個通知,和顯示在狀態條上的通知對象。

例如:

要使一個Service推出前臺運行,調用stopForeground(). 這個方法接受一個boolean參數,指明是否同時移除狀態條上的通知。 這個方法並不會停止Service的運行。但如果你停止一個在前臺運行的Service,那麼會同時移除狀態條的通知。

管理Service的生命週期

和Activity相比,Service的生命週期要簡單很多。但你更要關注你的Service是如何創建和銷燬的,這是因爲Service可以在用戶不知道的情況下在後臺運行。

Service的生命週期,從其創建後到銷燬,可以有兩條不同的路徑:

“Started”的Service 這個Service是由其它的應用程序組件調用startService()創建的,這個service 可以在後臺無限期運行直到調用 stopSelf()或者其它組件調用stopService()來停止它。當Service停止時,系統將銷燬這個Service。

支持“綁定”的Service 這種Service是在其它組件調用 bindService()綁定它時創建,客戶端然後可以通過服務接口和Serivce通信。客戶端可以調用 unbindService () 解除與Service的綁定。多個客戶端可以同時綁定同一Service,當一個Service沒有客戶端和它綁定時,系統則銷燬這個Service。

這兩條路徑並不是完全分開的,也就是說,你可以去綁定一個已經是“Started”狀態的Service。比如,一個通過媒體播放的Service可能通過指明需要播放音樂的Intent然後調用startService() 啓動的。再往後,用戶可能打算來操作媒體播放器,那麼一個Activity可以調用 bindServive()來綁定這個Service。在這種情況下,stopSelf()或stopSelf()並不真正停止Service直到所有的客戶端都解除與Service的綁定。

實現生命週期回調函數

和Activity類似,Service也提供了一些回調函數接口允許你重載這些方法來監視Service狀態的變化並添加合適的處理,下面代碼給出一個Service生命週期回調函數實現的框架:

  1. package com.example.bookdemo;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6.   
  7.   
  8. public class ExampleService extends Service {  
  9.     // indicates how to behave if the service is killed  
  10.     int mStartMode;    
  11.     // interface for clients that bind  
  12.     IBinder mBinder;       
  13.     // indicates whether onRebind should be used  
  14.     boolean mAllowRebind;   
  15.   
  16.     @Override  
  17.     public void onCreate() {  
  18.         // The service is being created  
  19.     }  
  20.     @Override  
  21.     public int onStartCommand(Intent intent,   
  22.             int flags, int startId) {  
  23.         // The service is starting,   
  24.         // due to a call to startService()  
  25.         return mStartMode;  
  26.     }  
  27.     @Override  
  28.     public IBinder onBind(Intent intent) {  
  29.         // A client is binding to the service   
  30.         // with bindService()  
  31.         return mBinder;  
  32.     }  
  33.     @Override  
  34.     public boolean onUnbind(Intent intent) {  
  35.         // All clients have unbound with unbindService()  
  36.         return mAllowRebind;  
  37.     }  
  38.     @Override  
  39.     public void onRebind(Intent intent) {  
  40.         // A client is binding to the service with bindService(),  
  41.         // after onUnbind() has already been called  
  42.     }  
  43.     @Override  
  44.     public void onDestroy() {  
  45.         // The service is no longer used and is being destroyed  
  46.     }  
  47. }  


  和Activty不同的是,Service的生命週期回調函數沒有必要調用基類的對於的方法。

 

圖 1.                     Servcie的生命週期

通過實現這些回調函數,你可以監視Service生命週期中兩個嵌套的循環:

·        整體生命週期 指Service在onCreate()和onDestroy()之間。和Activity類似,Service可以在onCreate () 進行一些初始化工作,而在onDestroy () 中釋放資源。比如,一個音樂播放器可以在onCreate() 創建用來播放音樂的線程,而在onDestroy() 停止這個線程。

·        活動生命週期 Service 在 onStartCommand() 或 onBind() 後開始活動,每個方法分別處理來自 startService和 bindService() 發過來請求 Intent。如果是“Started”的Service,那麼它活動的生命週期和Service的整個生命週期是一致的。如果是“綁定”的Service,那麼它的活動生命週期終止與unbind()。

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