服務

文章譯自:http://developer.android.com/guide/components/services.html

  1. 基礎知識
    1. 在清單文件中聲明服務
  2. 創建啓動的服務
    1. 擴展 IntentService
    2. 擴展 Service
    3. 啓動服務
    4. 停止服務
  3. 創建綁定的服務
  4. 向用戶發送通知
  5. 在前臺運行服務
  6. 管理服務的聲明週期
    1. 實現生命週期回調函數

快速預覽

  • 服務可以運行在後臺來執行操作,甚至是用戶處在不同的應用中;
  • 服務可以允許其他組件綁定到它,以此來與其交互並執行進程間的通信;
  • 默認情況下,服務運行在宿主應用的主線程中 ;

服務 是可以在後臺執行耗時操作且不提供用戶界面的應用組件。其它的應用組件可以啓動服務並且它將繼續運行在後臺,即使用戶切換至其它應用。此外,一個組件可以綁定到服務來與之交互,甚至是執行進程間通信(IPC)。例如,一個服務可能處理網絡事務,播放音樂,執行文件I/O,或者與一個內容提供者交互,所有這些都在後臺執行。


服務基本上可以採用兩種形式:


啓動的

當一個應用組件(比如一個活動)通過調用startService()來啓動服務時,此時服務爲“啓動的”。一旦啓動,服務就可以無限期地運行在後臺,即便是啓動它的組件被銷燬了。通常,一個啓動的服務執行一個單一操作且不向調用者返回結果。例如,它可能通過網絡下載或上傳文件。當操作完成時,服務應該自己停止。


綁定的

當一個應用組件通過調用bindService()綁定到它時,此時服務爲“綁定的”。綁定的服務提供了一個客戶端-服務器接口,它允許組件與服務進行交互:發送請求,獲取結果,甚至是通過進程間通信(IPC)跨進程地做到這些。綁定的服務僅在其它應用組件綁定到其上時才運行。多個組件可以同時綁定到服務上,僅當它們都取消綁定時,服務才被銷燬。


儘管該文檔單獨地概括性地論述了這兩種類型的服務,不過你的服務可以以兩種方式工作 —— 它可以被啓動(來無限期地運行)並且也允許進行綁定。這僅僅是你是否實現一對回調方法的問題:onStartCommand()允許組件來啓動它,onBind()則允許綁定。


無論你的應用是否是被啓動,被綁定,或二者皆是,任何應用組件都能夠以任何組件使用活動(通過使用Intent啓動它)的相同方式來使用該服務(甚至是來自不同的應用)。不過,你可以在清單文件內將服務聲明爲私有的,並阻止來自其他應用的訪問。這一點在關於在清單文件中聲明服務的章節內有更多討論。

 

注意: 服務運行在它的寄宿進程的主線程內,就是說,服務不會創建它自己的線程,並且不會運行在一個單獨的進程內(除非你另外指定)。這意味着,如果你的服務將要執行任何CPU密集型工作或者阻塞操作(諸如MP3回放或網絡)。通過使用一個單獨的線程,你將降低應用程序沒有響應(ANR)的錯誤風險,並且應用的主線程能夠依然致力於用戶與你的活動間的交互。


1.  基礎知識


爲了創建服務,你必須創建一個Service的子類(或一個它的現有子類)。在你的實現中,你需要重寫一些處理服務生命週期關鍵方面的回調方法以及爲組件綁定到該服務提供一種機制,如有需要的話。你應該重寫的最重要的回調方法是:


onStartCommand()

當另外的組件,諸如一個活動,通過調用startService()來請求啓動服務時,由系統調用這個方法。一旦執行此方法,服務被啓動並可以在後臺無限期地運行。如果你實現了這個方法,在服務的工作完事時,由你負責通過調用stopSelf()stopService()來停止服務。(如果你僅想提高綁定,則不必實現該方法。)

onBind()

當其它組件通過調用bindService()打算與服務綁定時(譬如執行RPC),由系統調用這個方法。在你的此方法實現裏,通過返回一個IBinder,你必須提供一個客戶端使用的來與服務器進行通話的接口。你通常必須實現這個方法,除非你不打算允許綁定,那麼你應該返回null。

onCreate()

當服務首次被創建來執行一次性設置過程時,由系統調用這個方法(此前它調用onStartCommand()onBind())。如果服務已經運行了,該方法不會被調用。

onDestroy()

當服務不再被使用並且正被銷燬時,由系統調用這個方法。你的服務應該實現這方法來清理任何諸如線程,註冊的監聽器,接收器等等。這是最後一個服務收到的調用。


如果組件通過調用startService()(它會導致一個對onStartCommand()的調用)啓動服務,那麼該服務會保持運行直到它使用stopSelf()結束其自己或者其它組件通過調用stopService()停止它。


如果組件調用bindService()來創建服務(onStartCommand()不會被調用),那麼該服務僅在組件綁定在其上時才運行。一旦該服務與全部客戶端脫離綁定,系統則銷燬它。


僅當在低內存時Android系統纔將強制停止服務,同時它必須爲擁有用戶焦點的活動恢復系統資源。如果服務被綁定到擁有用戶焦點的活動上,那麼它不大可能被殺死,以及如果服務被聲明爲在前臺運行(稍後討論),那麼它將幾乎從不會被殺死。另外,如果服務被啓動了並且是長時間運行的,那麼隨着時間的流逝,系統將降低它在後臺任務列表中的位置並且該服務將變得非常容易殺死,因此,如果你的服務被啓動了,那麼你必須設計它來得體地處理由系統導致的重啓。如果系統殺死了你的服務,資源一旦再次變得可用(儘管這還取決於你從onStartCommand()返回的值,如稍後的討論)系統就重新啓動該服務。更多關於當系統可能銷燬一個服務的信息,請參閱進程和線程文檔。


在接下來的章節裏,你將看到如何能夠創建每種類型的服務以及如何由其他應用組件來使用它。


1.1  在清單文件中聲明服務


像活動(及其它組件)一樣,你必須在應用的清單文件中聲明所有的服務。


爲了聲明你的服務,添加<service>元素作爲<application>元素的子元素。例如:


<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

還有其它的你可以包含在<service>元素內的屬性來定義諸如啓動服務所需要的權限及服務應該運行於此的進程等屬性。android:name是唯一必需的屬性,因爲它指定了服務的類名。一旦你發佈了你的應用,你就不應該改變這個名字,因爲如果你這樣做了,可能會破壞在那裏使用顯式意圖引用你的服務的某些功能(閱讀博客文章,不能改變的東西)。


更多關於在清單文件內聲明服務的信息,請參閱<service>元素參考。


正如活動一樣,服務可以定義允許其它組件使用顯式意圖來調用服務的意圖過濾器。通過聲明意圖過濾器,來自安裝於用戶設備上的任何應用組件都可能啓動你的服務,如果你的服務聲明瞭匹配其它應用遞送給startService()的意圖的意圖過濾器。


如果你打算只在本地使用你的服務(其它應用不使用它),那麼你不必(也不應該)提供任何意圖過濾器。沒有任何意圖過濾器,你必須使用一個顯式地指定服務類的意圖來啓動服務。更多關於啓動服務的信息將在下面討論。


此外,你可以確保你的服務是你的應用私有的,只要你包含android:exported屬性並設置它爲“false”。即使你的服務提供了意圖過濾器,此設置也是有效的。


更多關於爲你的服務創建意圖過濾器的信息,請參閱意圖和意圖過濾器文檔。


你應該使用服務還是線程?


服務是一個簡單的組件,它可以在後臺運行即使當用戶沒有與你的應用進行交互時。因此,只要這正是你所需要的,你就應該創建服務。


如果你需要在你的主線程之外執行操作,但僅當用戶與你的應用交互時,那麼你很可能應該創建一個新的線程而不是一個服務。例如,如果你想播放一些音樂,但僅當你的活動正在運行的時候,你可能在onCreate()內創建一個線程,並在onStart()內開始運行它,然後在onStop()裏停止它。還可以考慮使用AsyncTaskHandlerThread而不是傳統的Thread類。更多關於線程的信息,請參閱進程和線程文檔。


記住,如果你使用服務,默認情況下,它依然運行在你的應用的主線程內,所以,如果服務執行密集或阻塞操作的話,你還是應該在服務內部創建一個新的線程。



2. 創建啓動的服務


啓動的服務就是其他組件通過調用startService()(導致對服務的onStartCommand()的方法的調用)啓動的服務。當服務被啓動時,它便擁有一個獨立於啓動它的組件的生命週期並且無限期地運行於後臺,即便是啓動它的組件被銷燬。因此,服務應該在完成其工作時通過調用stopSelf()停止自己,或者,其它組件可以通過調用stopService()停止它。諸如活動這樣的應用組件可以通過調用startService()並傳遞一個指定了服務和包含了任何供服務使用的數據的Intent來啓動服務。服務則在onStartCommand()內接收這個Intent


例如,假設一個活動需要把一些數據保存到網上數據庫。該活動可以通過向startService()傳遞一個意圖來啓動一個配套服務並向其遞送要保存的數據。服務在onStartCommand()內接收該意圖,然後連接互聯網並執行數據庫事務。當事務完成時,服務停止其自己然後被銷燬。

 

注意: 默認情況下,服務運行在與聲明它的應用程序相同的進程裏,並且在該應用程序的主線程裏。所以,當用戶與來自相同應用內的活動交互時,如果你的服務執行密集或阻塞操作,則該服務將減緩活動執行。爲了避免影響應用程序執行,你應該在該服務的內部啓動一個新的線程。

 

針對android 1.6或更低版本

如果你正在構建針對Android 1.6或更低版本的應用程序,你需要實現onStart(),而不是onStartCommand()(在Android 2.0上,onStart()已被棄用,推薦使用onStartCommand())

更多關於爲早於Android2.0的版本提供兼容的信息,請參閱onStartCommand()文檔。


傳統上,有兩個你可以擴展的類來創建啓動的服務:


Service

它是所有服務的基類。當你擴展此類時,重要的是,你要創建一個在其內完成服務的所有工作的新線程,因爲,默認情況下,服務使用應用的主線程,它可能阻礙任何你的應用正在運行的活動的執行。


IntentService

它是Service的一個子類,它使用一個輔助線程來逐一地處理所有的啓動請求。這是最好的選擇,如果你不需要你的服務同時處理多個請求的話。 所有你需要做的就是實現onHandleIntent(),它接收每個啓動請求的意圖以便你可以執行後臺操作。


2.1  擴展IntentService 類


因爲絕大多數啓動的服務不需要同時處理多個請求(這實際上是一個危險的多線程情況), 如果你使用IntentService類來實現你的服務,這可能是最好的。


IntentService 執行以下操作:

  • 創建一個默認的獨立於應用程序主線程的輔助線程,該線程處理全部遞送給onStartCommand()的意圖。
  • 創建一個工作隊列,它每次向你的onHandleIntent()實現遞送一個意圖,所以你從不用擔心多線程的問題。
  • 待所有啓動請求被處理完成後,停止服務,所以你從不需要調用stopSelf()
  • 提供默認的返回null的onBind() 實現。
  • 提供一個默認的onStartCommand()實現,它向工作隊列發送意圖,然後發送到onHandleIntent() 實現。


所有的這一切表明了這樣的事實,你需要做的就是實現onHandleIntent() 來完成由客戶端的制定的工作。(不過,你還需要爲服務提供一個小的構造函數。)


下面是一個IntentService實現的例子:

public class HelloIntentService  extends  IntentService {  
  
  /**  
  * 構造器是必的,同時必須使用輔助線程的名字調用基類IntentService(String) 構造器。
  */  
  public HelloIntentService() {  
      super("HelloIntentService");  
  }  
  
  /** 
   * IntentService從默認的輔助線程裏使用啓動服務的意圖調用這個方法。當該方法返回時,
  * IntentService 適當地停止服務。
  */  
  @Override  
  protected void onHandleIntent(Intent intent) {  
      //通常,我們在這裏執行某些操作,如下載文件。
      // 對於我們的例子來說,我們只是休眠5秒鐘。  
long endTime = System.currentTimeMillis() + 5*1000;  
      while (System.currentTimeMillis() < endTime) {  
          synchronized (this) {  
              try {  
                  wait(endTime - System.currentTimeMillis());  
              } catch (Exception e) {  
              }  
          }  
      }  
  }  
}

這就是你所需要的:一個構造函數和一個onHandleIntent()實現。


如果你還打算重寫其他的回調方法,譬如onCreate()onStartCommand(), 或onDestroy(),請務必要調用它們在基類中的實現,這樣做是爲了IntentService可以恰當地處理輔助線程的生命。


例如,onStartCommand()必須返回默認的實現(它就是意圖如何被遞送給onHandleIntent()的實現)

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

onHandleIntent()以外,唯一你不需要從基類調用的方法是onBind()(而是僅需要實現它,如果你的服務允許綁定的話)。


在接下來的章節裏,你將看到當擴展基礎類Service時相同類型的服務是如何實現的,擴展Service的代碼要多很多,但是如果你需要處理同步的啓動請求,擴展Service可能是合適的選擇。


2.2  擴展Service類


正如上一章節所見,使用IntentService可以非常簡單地實現一個啓動的服務。然而,如果你要求你的服務去執行多線程(而不是通過工作隊列來處理啓動請求),那麼你可以擴展Service類來處理每一個意圖。


爲了便於比較,下面的示例代碼是一個Service類的擴展實現,它執行同上面使用IntentService的示例完全一樣的工作。也就是說,對於每個啓動請求,它使用一個輔助線程來完成工作並且一次只處理一個請求。

public class HelloService extends Service {  
  private Looper mServiceLooper;  
  private ServiceHandler mServiceHandler;  
  
  // 接收來自輔助線程的處理器 
  private final class ServiceHandler extends Handler {  
      public ServiceHandler(Looper looper) {  
          super(looper);  
      }  
      @Override  
      public void handleMessage(Message msg) {  
          //通常,我們將會在此處完成某些工作, 例如下載一個文件.  
           // 對於我們示例來說,我們在此只是休眠5秒鐘. 
          long endTime = System.currentTimeMillis() + 5*1000;  
          while (System.currentTimeMillis() < endTime) {  
              synchronized (this) {  
                  try {  
                      wait(endTime - System.currentTimeMillis());  
                  } catch (Exception e) {  
                  }  
              }  
          }  
          // 利用啓動ID停止服務,以便我們在處理其它工作期間不停止該服務
          stopSelf(msg.arg1);  
      }  
  }  
  
  @Override  
  public void onCreate() {  
    //啓動運行服務的線程。注意,我創建一個獨立的線程,因爲服務通常運行於進程的主線程裏,
    //我們不想阻塞它。我們還賦予它後臺優先權,這樣CPU密集型的工作將不會中斷我們的UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",  
            Process.THREAD_PRIORITY_BACKGROUND);  
    thread.start();  
      
    //獲取HandlerThread的Looper,接着把它用於我們的Handler。   
    mServiceLooper = thread.getLooper();  
    mServiceHandler = new ServiceHandler(mServiceLooper);  
  }  
  
  @Override  
  public int onStartCommand(Intent intent, int flags, int startId) {  
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();  
  
      // 對於每個啓動請求, 發送一條消息來啓動工作並遞送啓動ID,所以當我們完成該工作時,
     //我們知道我們正在停止的是哪個請求
      Message msg = mServiceHandler.obtainMessage();  
      msg.arg1 = startId;  
      mServiceHandler.sendMessage(msg);  
        
      //如果服務被殺死了,從這返回後會從新啓動
      return START_STICKY;  
  }  
  
  @Override  
  public IBinder onBind(Intent intent) {  
      //我們沒有提供綁定,所以返回null  
      return null;  
  }  
    
  @Override  
  public void onDestroy() {  
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();   
  }  
}

正如你所看到的,它比使用IntentService有更多的操作。


然而,由於你親自處理每個onStartCommand()調用,因此你可以同時執行多個請求。這個示例並不是那樣的,但是如果你打算那樣,那麼你可以爲每個請求創建一個新的線程,然後立即運行它們(而不是等待先前的請求結束)。


注意,onStartCommand()方法必須返回一個整數。該整數是一個描述在出現系統殺死服務的事件時系統該如何繼續服務的數值(如上所述,IntentService的默認實現爲你處理這一點,不過你可以修改它)。由onStartCommand()返回的值必須是以下常量中的一個:


START_NOT_STICKY

onStartCommand()返回後,如果系統將服務殺死,則不再重新創建該服務,除非有未決的意圖遞送。當在沒必要以及你的應用可以簡單地重啓任何未完成的任務時,它是避免運行你的服務的最安全的選項。


START_STICKY

onStartCommand()返回後,如果系統將服務殺死,則重建該服務並調用onStartCommand(),但是不遞送最後的意圖。相反,系統使用一個空的意圖來調用onStartCommand(),除非有未決的意圖來啓動該服務,在這種情況下,遞送那些意圖。這適合於不執行命令的媒體器(或類似的服務),而是無限期地運行並等待着工作。


START_REDELIVER_INTENT

onStartCommand()返回後,如果系統將服務殺死,則重建服務並使用最後遞送給該服務的意圖調用onStartCommand()。任何未決的意圖依次地遞送給服務。這適合於主動執行應該立即被恢復的工作的服務,譬如下載文件。


更多關於這些返回值的細節,請參閱每個常量連接的參考文檔。


2.3  啓動服務


可以通過向startService()傳遞一個Intent(其指定了啓動的服務)從活動或其他應用組件內啓動服務。Android系統調用服務的onStartCommand()方法並把Intent傳遞給它。(絕不應該直接調用onStartCommand()。)


例如,與startService()一同使用顯式意圖,活動可以啓動上一章節內的示例服務(HelloSevice)。

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


startService()方法立刻返回,接着android系統調用服務的onStartCommand()方法。如果服務尚未運行,系統則首先調用onCreate(),然後調用onStartCommand()

 

如果服務也沒有提供綁定,通過startService()遞送的意圖則是應用組件與服務間通信的唯一方式。然而,如果你打算讓服務返回一個結果,那麼啓動服務的客戶端創建一個廣播(使用getBroadcast())PendingIntent,然後把它遞送給在意圖內啓動的服務。於是服務可以利用廣播來傳遞結果。


多個啓動服務的請求會導致多個相應的對服務的onStartCommand()的調用。但是僅需要一個停止服務的請求(通過stopSelf() or stopService())便可以停止它。


2.4  停止服務


啓動的服務必須管理它自己的生命週期。就是說,系統不會停止或銷燬服務,除非系統必須恢復系統內存然後服務在onStartCommand()返回後繼續運行。因此,服務必須通過調用stopSelf()來停止自己,或其他組件可以通過調用stopService()來停止它。


一旦使用stopSelf()stopService()請求停止服務,系統會竟可能快地銷燬它。


然而,如果你的服務同時處理多個onStartCommand()請求,那麼你不應該在完成了處理一個啓動請求時停止該服務,因爲自那以後你也許已經收到新的啓動請求(在第一個請求之後的停止將終止第二個請求)。爲了避免這個問題,可以使用stopSelf(int)來確保你的停止服務的請求總是基於最近的啓動請求。也就是說,當你調用stopSelf(int)時,傳送啓動請求的ID給停止請求對應的該方法。在調用stopSelf(int)時,如果服務收到一個新的啓動請求,那麼ID將不匹配,則服務將不會停止


注意: 這點很重要,爲了避免浪費系統資源和消耗電池電量,在當服務完成工作時,你的應用停止它自己的服務。如有必要,其他組件可以通過調用stopService()來停止服務。甚至如果你啓用服務綁定,即使它從未收到onStartCommand()調用,你通常必須自己停止該服務。


更多關於服務生命週期的信息,請參閱下面的關於管理服務生命週期的章節。


3.創建綁定的服務


綁定的服務是允許應用組件通過調用bindService()綁定到它以便創建一個長期連接(並且通常不允許組件通過調用startService()來啓動它)的服務。


當你打算通過進程間通訊(IPC)的方式從應用內的活動和其他組件與服務交互或者把應用的功能暴露給其他應用時,你應該創建一個綁定的服務。


爲創建綁定的服務,你必須實現onBind()回調方法以返回一個定義了與服務溝通的接口的IBinder。於是其他應用組件就可以調用bindService()來取回該接口,並開始調用服務上的方法。該服務僅爲服務於綁定到其上的應用組件而存活,所以當沒有組件綁定到服務時,系統便銷燬它(你不必以當通過onStartCommand()啓動服務時你必須停止的方式來停止綁定的服務)。


爲創建綁定的服務,首先必須做的是定義指定客戶端如何可以與服務進行通信的接口。這個在服務與客戶端之間的接口必須是一個IBinder的實現,並且是你的服務從onBind()回調方法返回的東西。一旦客戶端收到IBinder,它便可以通過這個接口開始與服務進行交互。


多個客戶端可以同時綁定到服務上。當一個客戶端完成同服務交互時,它調用unbindService()來解除綁定。一旦沒有客戶端綁定到服務上時,系統銷燬服務。


有多種方式實現綁定的服務,並且實現起來比實現啓動的服務要複雜的多,所以關於綁定的服務的討論放在一個單獨的關於綁定的服務的文檔裏


4.向用戶發送通知


一旦運行,服務可以通過使用消息框通知(Toast Notifications)或狀態欄通知(Status Bar Notifications)通知用戶。


消息框通知是一條短暫出現在當前窗口表面上的消息,然後便消失了,而狀態欄通知則使用消息在狀態欄上提供一個圖標,用戶可以進行選擇它以便採取行動(比如開啓一個活動)。


通常,當一些後臺工作完成 (譬如文件下載完成)並且用戶現在可以在其上執行操作時,狀態欄通知是最好的方式。當用戶從展開的視圖中選擇通知時,此通知就可以啓動活動(比如查看下載的文件)。


更多信息請參與消息框通知(Toast Notifications)或狀態欄通知(Status Bar Notifications)開發者指南


5.在前臺運行服務


前臺服務是一個被視作是用戶主動意識到的並且因此不作爲在系統低內存時會被系統殺死的候選者的服務。前臺服務必須提供一個狀態欄通知,它被放置在“Ongoing”標題之下,這意味着該通知不能被忽略,除非服務要麼是被停止要麼是從前臺被移除。


例如,從服務開始播放音樂的音樂播放器應該被設置運行在前臺,因爲用戶清楚地知道它的操作。位於狀態欄上的通知可能提示當前的歌曲,並允許用戶啓動一個活動來與音樂播放器交互。


爲使你的服務運行在前臺,調用startForeground()。這個方法攜帶兩個參數:一個唯一地標識通知的整數和狀態欄的通知(Notification)。例如:


Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION, notification);

爲從前臺移除服務,調用stopForeground()。這個方法攜帶一個boolean,暗指是否也移除狀態欄通知。這個方法不會停止服務。然而,當服務正在前臺運行時,如果你停止了它,那麼通知也會被移除。


注意: startForeground()stopForeground()方法是在Android 2.0(API Level5)中被引入的。爲在較老版本平臺的前臺運行你的服務,你必須使用先前的setForeground()方法 — 更多關於如何提供向後兼容的信息,參閱startForeground()文檔。


更多關於通知的信息,參閱創建狀態欄通知


6.管理服務的生命週期


服務的生命週期比活動的生命週期簡單的多。可是,它甚至更重要,你需要密切注意你的服務是如何被創建及被銷燬的,因爲運行於後臺的服務而沒讓用戶時刻注意到。

服務生命週期 —— 從它被創建時到它被銷燬時 —— 可以遵循兩個不同的路經:

  • 啓動的服務

       當其他組件調用startService()時服務被創建。然後服務無限期地運行,並必須通過調用stopSelf()來停止自己。其他組件也可以通過調用stopService()停止服務。當服務被停止時,系統銷燬它。

  • 綁定的服務

       當其他組件(客戶端)調用bindService(),服務被創建。接着,客戶端通過一個IBinder接口與服務進行通信。客戶端可以通過調用unbindService()關閉連接。多個客戶端可以綁定到相同的服務上,並且當他們全部都解除綁定時,系統銷燬該服務。(服務不必停止它自己。)


這兩個路徑不是完全獨立的。也就是說,你可以綁定到一個已經通過startService()啓動的服務。例如,可以通過使用指明要播放的音樂的意圖來調用startService()啓動後臺音樂服務。之後,可能當用戶想在播放器上行使某些控制或獲取關於當前曲目信息時,活動可以通過調用bindService()綁定到服務上。在類似於此的情況下,stopService()stopSelf()實際上不會停止服務,知道全部客戶端解除綁定。


6.1 實現生命週期回調


如同活動一樣,服務擁有生命週期回調方法,你可以實現它們來控制服務狀態的變化以及在適當的時候執行操作。接下來的框架服務展示了生命週期的每個方法:


public class ExampleService extends Service {  
    int mStartMode;       // 指明如果服務被殺後該如何執行
    IBinder mBinder;      //客戶端綁定的接口   
    boolean mAllowRebind; //指明是否應該使用onRebind
  
    @Override  
    public void onCreate() {  
        // 服務正被創建
    }  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        // 服務正在啓動,由於一個 startService()  的調用        
return mStartMode;  
    }  
    @Override  
    public IBinder onBind(Intent intent) {  
        // 客戶端 正通過bindService()綁定到服務上  
        return mBinder;  
    }  
    @Override  
    public boolean onUnbind(Intent intent) {  
        //全部的客戶端通過 unbindService()已經解除綁定  
        return mAllowRebind;  
    }  
    @Override  
    public void onRebind(Intent intent) {  
        // onUnbind() 已經被調用後,  
         // 客戶端正通過bindService()綁定服務上 
    }  
    @Override  
    public void onDestroy() {  
        // 不再使用服務,並正在被銷燬
    }  
}

注意: 不像活動的生命週期回調方法, 你不必調用那些回調方法的基類的實現。



表 2. 服務的生命週期。左圖顯示了當服務通過startService()被創建時的生命週期,右圖顯示當服務通過bindService()被創建時的生命週期。


通過實現這些方法,你可以控制服務生命週期的兩個嵌套的循環:

  • 服務的整個生命期(entire lifetime)發生在onCreate()被調用時和onDestroy()返回時之間。同活動一樣,服務在onCreate()裏執行它的初始的安裝並在onDestroy()內釋放所有餘下的資源。例如,音樂播放服務可以在onCreate()內創建線程,並在此線程裏播放音樂,然後在onDestroy()內停止線程。

        所有服務都要調用onCreate()onDestroy()方法,無論它們是通過startService() 還是bindService()創建的。


        如果服務被啓動了,活動生命期整個生命期同時結束(服務依然是活動的,甚至在onStartCommand()返回之後)。如果服務被綁定了,活動生命期在onUnbind()返回時結束。


注意: 儘管啓動的服務可以通過調用stopSelf()stopService()被停止,但是服務並沒有各自的回調(沒有onStop()回調)。所以,除非服務被綁定到客戶端上,否則系統在當服務被停止時(onDestroy()是唯一接收的回調)銷燬它。


圖2說明了服務的標準回調函數。儘管該圖把通過startService()創建的服務從那些通過bindService()創建的服務中分開了,但是請記住,任何服務,不管是它是如何被創建的,都可能允許客戶端綁定到它。所以,一個最初由onStartCommand()啓動的服務(通過客戶端調用startService())依然可以接收一個onBind()調用(當客戶端調用bindService()時)。


更多關於創建一個提供綁定的服務的信息,請參閱綁定的服務文檔,它在關於管理綁定的服務的生命週期章節裏包括了更多關於onRebind()回調方法的信息。

 

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