四大元件之 Service

嶽小川發表於2017-02-12

四大元件之 Service

[文章內容來自Developers]

Service是一個可以在後臺執行長時間執行操作而不提供使用者介面的應用元件。服務可由其他應用元件啟動,而且即使使用者切換到其他應用,服務仍將在後臺繼續執行。 此外,元件可以繫結到服務,以與之進行互動,甚至是執行程式間通訊 (IPC)。 例如,服務可以處理網路事務、播放音樂,執行檔案 I/O 或與內容提供程式互動,而所有這一切均可在後臺進行。
服務基本上分為兩種形式:
啟動
當應用元件(如 Activity)通過呼叫 startService()啟動服務時,服務即處於“啟動”狀態。一旦啟動,服務即可在後臺無限期執行,即使啟動服務的元件已被銷燬也不受影響。 已啟動的服務通常是執行單一操作,而且不會將結果返回給呼叫方。例如,它可能通過網路下載或上傳檔案。 操作完成後,服務會自行停止執行。
繫結
當應用元件通過呼叫 bindService()繫結到服務時,服務即處於“繫結”狀態。繫結服務提供了一個客戶端-伺服器介面,允許元件與服務進行互動、傳送請求、獲取結果,甚至是利用程式間通訊 (IPC) 跨程式執行這些操作。 僅當與另一個應用元件繫結時,繫結服務才會執行。 多個元件可以同時繫結到該服務,但全部取消繫結後,該服務即會被銷燬。

雖然本文件是分開概括討論這兩種服務,但是您的服務可以同時以這兩種方式執行,也就是說,它既可以是啟動服務(以無限期執行),也允許繫結。問題只是在於您是否實現了一組回撥方法:onStartCommand()(允許元件啟動服務)和 onBind()(允許繫結服務)。
無論應用是處於啟動狀態還是繫結狀態,抑或處於啟動並且繫結狀態,任何應用元件均可像使用 Activity 那樣通過呼叫 Intent來使用服務(即使此服務來自另一應用)。 不過,您可以通過清單檔案將服務宣告為私有服務,並阻止其他應用訪問。

注意:服務在其託管程式的主執行緒中執行,它既建立自己的執行緒,也在單獨的程式中執行(除非另行指定)。 這意味著,如果服務將執行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或聯網),則應在服務內建立新執行緒來完成這項工作。通過使用單獨的執行緒,可以降低發生“應用無響應”(ANR) 錯誤的風險,而應用的主執行緒仍可繼續專注於執行使用者與 Activity 之間的互動。

###基礎知識

要建立服務,您必須建立 Service的子類(或使用它的一個現有子類)。在實現中,您需要重寫一些回撥方法,以處理服務生命週期的某些關鍵方面並提供一種機制將元件繫結到服務(如適用)。 應重寫的最重要的回撥方法包括:

onStartCommand()
當另一個元件(如 Activity)通過呼叫 startService()請求啟動服務時,系統將呼叫此方法。一旦執行此方法,服務即會啟動並可在後臺無限期執行。 如果您實現此方法,則在服務工作完成後,需要由您通過呼叫 stopSelf()或 stopService()來停止服務。(如果您只想提供繫結,則無需實現此方法。)

onBind()
當另一個元件想通過呼叫 bindService()與服務繫結(例如執行 RPC)時,系統將呼叫此方法。在此方法的實現中,您必須通過返回 IBinder提供一個介面,供客戶端用來與服務進行通訊。請務必實現此方法,但如果您並不希望允許繫結,則應返回 null。

onCreate()
首次建立服務時,系統將呼叫此方法來執行一次性設定程式(在呼叫 onStartCommand()或 onBind()之前)。如果服務已在執行,則不會呼叫此方法。

onDestroy()
當服務不再使用且將被銷燬時,系統將呼叫此方法。服務應該實現此方法來清理所有資源,如執行緒、註冊的偵聽器、接收器等。 這是服務接收的最後一個呼叫。

如果元件通過呼叫 startService()啟動服務(這會導致對 onStartCommand()的呼叫),則服務將一直執行,直到服務使用 stopSelf()自行停止執行,或由其他元件通過呼叫 stopService()停止它為止。
如果元件是通過呼叫 bindService()來建立服務(且呼叫 onStartCommand(),則服務只會在該元件與其繫結時執行。一旦該服務與所有客戶端之間的繫結全部取消,系統便會銷燬它。
僅當記憶體過低且必須回收系統資源以供具有使用者焦點的 Activity 使用時,Android 系統才會強制停止服務。如果將服務繫結到具有使用者焦點的 Activity,則它不太可能會終止;如果將服務宣告為在前臺執行(稍後討論),則它幾乎永遠不會終止。或者,如果服務已啟動並要長時間執行,則系統會隨著時間的推移降低服務在後臺任務列表中的位置,而服務也將隨之變得非常容易被終止;如果服務是啟動服務,則您必須將其設計為能夠妥善處理系統對它的重啟。 如果系統終止服務,那麼一旦資源變得再次可用,系統便會重啟服務(不過這還取決於從 onStartCommand()返回的值,本文稍後會對此加以討論)。

您應使用服務還是執行緒?
簡單地說,服務是一種即使使用者未與應用互動也可在後臺執行的元件。 因此,您應僅在必要時才建立服務。
如需在主執行緒外部執行工作,不過只是在使用者正在與應用互動時才有此需要,則應建立新執行緒而非服務。 例如,如果您只是想在 Activity 執行的同時播放一些音樂,則可在 onCreate()中建立執行緒,在 onStart()中啟動執行緒,然後在 onStop()中停止執行緒。您還可以考慮使用 AsyncTask或 HandlerThread,而非傳統的 Thread類。
請記住,如果您確實要使用服務,則預設情況下,它仍會在應用的主執行緒中執行,因此,如果服務執行的是密集型或阻止性操作,則您仍應在服務內建立新執行緒。

在下文中,您將瞭解如何建立各類服務以及如何從其他應用元件使用服務。

使用清單檔案宣告服務
如同 Activity(以及其他元件)一樣,您必須在應用的清單檔案中宣告所有服務。
要宣告服務,請新增 元素作為 元素的子元素。例如:

<manifest ... > 
 ...  
<application ... >      
<service android:name=".ExampleService" />      
...  
</application>
</manifest>複製程式碼

您還可將其他屬性包括在 元素中,以定義一些特性,如啟動服務及其執行所在程式所需的許可權。android:name 屬性是唯一必需的屬性,用於指定服務的類名。應用一旦釋出,即不應更改此類名,如若不然,可能會存在因依賴顯式 Intent 啟動或繫結服務而破壞程式碼的風險(請閱讀部落格文章Things That Cannot Change[不能更改的內容])。
為了確保應用的安全性,請始終使用顯式 Intent 啟動或繫結 Service,且不要為服務宣告 Intent 過濾器。 啟動哪個服務存在一定的不確定性,而如果對這種不確定性的考量非常有必要,則可為服務提供 Intent 過濾器並從 Intent中排除相應的元件名稱,但隨後必須使用 setPackage()方法設定 Intent 的軟體包,這樣可以充分消除目標服務的不確定性。
此外,還可以通過新增 android:exported屬性並將其設定為 "false",確保服務僅適用於您的應用。這可以有效阻止其他應用啟動您的服務,即便在使用顯式 Intent 時也如此。

###建立啟動服務

啟動服務由另一個元件通過呼叫 startService()啟動,這會導致呼叫服務的 onStartCommand()方法。
服務啟動之後,其生命週期即獨立於啟動它的元件,並且可以在後臺無限期地執行,即使啟動服務的元件已被銷燬也不受影響。 因此,服務應通過呼叫 stopSelf()結束工作來自行停止執行,或者由另一個元件通過呼叫 stopService()來停止它。
應用元件(如 Activity)可以通過呼叫 startService()方法並傳遞 Intent物件(指定服務幷包含待使用服務的所有資料)來啟動服務。服務通過 onStartCommand()方法接收此 Intent。
例如,假設某 Activity 需要將一些資料儲存到線上資料庫中。該 Activity 可以啟動一個協同服務,並通過向 startService()傳遞一個 Intent,為該服務提供要儲存的資料。服務通過 onStartCommand()接收 Intent,連線到網際網路並執行資料庫事務。事務完成之後,服務會自行停止執行並隨即被銷燬。

注意:預設情況下,服務與服務宣告所在的應用執行於同一程式,而且執行於該應用的主執行緒中。 因此,如果服務在使用者與來自同一應用的 Activity 進行互動時執行密集型或阻止性操作,則會降低 Activity 效能。 為了避免影響應用效能,您應在服務內啟動新執行緒。

從傳統上講,您可以擴充套件兩個類來建立啟動服務:

Service
這是適用於所有服務的基類。擴充套件此類時,必須建立一個用於執行所有服務工作的新執行緒,因為預設情況下,服務將使用應用的主執行緒,這會降低應用正在執行的所有 Activity 的效能。

IntentService
這是 Service的子類,它使用工作執行緒逐一處理所有啟動請求。如果您不要求服務同時處理多個請求,這是最好的選擇。 您只需實現 onHandleIntent()方法即可,該方法會接收每個啟動請求的 Intent,使您能夠執行後臺工作。

下文介紹如何使用其中任一個類來實現服務。

擴充套件 IntentService 類
由於大多數啟動服務都不必同時處理多個請求(實際上,這種多執行緒情況可能很危險),因此使用 類實現服務也許是最好的選擇。
IntentService執行以下操作:

  • 建立預設的工作執行緒,用於在應用的主執行緒外執行傳遞給 onStartCommand() 的所有 Intent。
  • 建立工作佇列,用於將 Intent 逐一傳遞給 onHandleIntent()實現,這樣您就永遠不必擔心多執行緒問題。
  • 在處理完所有啟動請求後停止服務,因此您永遠不必呼叫 stopSelf()。
  • 提供 onBind()的預設實現(返回 null)。
  • 提供 onStartCommand()的預設實現,可將 Intent 依次傳送到工作佇列和 onHandleIntent()實現。

綜上所述,您只需實現 onHandleIntent()來完成客戶端提供的工作即可。(不過,您還需要為服務提供小型建構函式。)
以下是 IntentService的實現示例:

public class HelloIntentService extends IntentService {  
/**   
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.   
*/  
public HelloIntentService() {      
super("HelloIntentService");  }  
/**   
* The IntentService calls this method from the default worker thread with   
* the intent that started the service. When this method returns, IntentService   
* stops the service, as appropriate.   
*/  
@Override  
protected void onHandleIntent(Intent intent) {      
// Normally we would do some work here, like download a file.    
  // For our sample, we just sleep for 5 seconds.      
try {          
Thread.sleep(5000);      
} catch (InterruptedException e) {          
// Restore interrupt status.          
Thread.currentThread().interrupt();      
}  
}}複製程式碼

您只需要一個建構函式和一個 onHandleIntent()實現即可。
如果您決定還重寫其他回撥方法(如 onCreate()、onStartCommand()或 onDestroy()),請確保呼叫超類實現,以便 IntentService能夠妥善處理工作執行緒的生命週期。
例如,onStartCommand()必須返回預設實現(即,如何將 Intent 傳遞給 onHandleIntent():

@Overridepublic 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基類時實現同類服務。該基類包含更多程式碼,但如需同時處理多個啟動請求,則更適合使用該基類。

擴充套件服務類

正如上一部分中所述,使用 IntentService顯著簡化了啟動服務的實現。但是,若要求服務執行多執行緒(而不是通過工作佇列處理啟動請求),則可擴充套件 Service類來處理每個 Intent。
為了便於比較,以下提供了 Service類實現的程式碼示例,該類執行的工作與上述使用 IntentService的示例完全相同。也就是說,對於每個啟動請求,它均使用工作執行緒執行作業,且每次僅處理一個請求。

public class HelloService extends Service {  
private Looper mServiceLooper;  
private ServiceHandler mServiceHandler;  
// Handler that receives messages from the thread  
private final class ServiceHandler extends Handler {      
public ServiceHandler(Looper looper) {          
super(looper);      
}      
@Override      
public void handleMessage(Message msg) {          
// Normally we would do some work here, like download a file.    
      // For our sample, we just sleep for 5 seconds.          
try {              
Thread.sleep(5000);          
} catch (InterruptedException e) {              
// Restore interrupt status.              Thread.currentThread().interrupt();          
}          
// Stop the service using the startId, so that we don't stop          
// the service in the middle of handling another job          
stopSelf(msg.arg1);      
}  
}  
@Override  
public void onCreate() {    
// Start up the thread running the service.  Note that we create a    
// separate thread because the service normally runs in the process's    
// main thread, which we don't want to block.  We also make it    
// background priority so CPU-intensive work will not disrupt our UI.    
HandlerThread thread = new HandlerThread("ServiceStartArguments",            Process.THREAD_PRIORITY_BACKGROUND);    
thread.start();    
// Get the HandlerThread's Looper and use it for our 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();      
// For each start request, send a message to start a job and deliver the      
// start ID so we know which request we're stopping when we finish the job      
Message msg = mServiceHandler.obtainMessage();      
msg.arg1 = startId;      
mServiceHandler.sendMessage(msg);      
// If we get killed, after returning from here, restart      
return START_STICKY;  
}  
@Override  
public IBinder onBind(Intent intent) {      
// We don't provide binding, so return 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()返回後終止服務,則除非有掛起 Intent 要傳遞,否則系統不會重建服務。這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啟所有未完成的作業時執行服務。

START_STICKY
如果系統在 onStartCommand()返回後終止服務,則會重建服務並呼叫 onStartCommand(),但不會重新傳遞最後一個 Intent。相反,除非有掛起 Intent 要啟動服務(在這種情況下,將傳遞這些 Intent ),否則系統會通過空 Intent 呼叫 onStartCommand()。這適用於不執行命令、但無限期執行並等待作業的媒體播放器(或類似服務)。

START_REDELIVER_INTENT
如果系統在 onStartCommand()返回後終止服務,則會重建服務,並通過傳遞給服務的最後一個 Intent 呼叫 onStartCommand()。任何掛起 Intent 均依次傳遞。這適用於主動執行應該立即恢復的作業(例如下載檔案)的服務。

啟動服務

您可以通過將 Intent(指定要啟動的服務)傳遞給 startService(),從 Activity 或其他應用元件啟動服務。Android 系統呼叫服務的 onStartCommand()方法,並向其傳遞 Intent。(切勿直接呼叫 onStartCommand()。)
例如,Activity 可以結合使用顯式 Intent 與 startService(),啟動上文中的示例服務 (HelloService):

Intent intent = new Intent(this, HelloService.class);
startService(intent);複製程式碼

startService()方法將立即返回,且 Android 系統呼叫服務的 onStartCommand()方法。如果服務尚未執行,則系統會先呼叫 onCreate(),然後再呼叫 onStartCommand()。
如果服務亦未提供繫結,則使用 startService()傳遞的 Intent 是應用元件與服務之間唯一的通訊模式。但是,如果您希望服務返回結果,則啟動服務的客戶端可以為廣播建立一個 PendingIntent(使用 getBroadcast()),並通過啟動服務的 Intent傳遞給服務。然後,服務就可以使用廣播傳遞結果。
多個服務啟動請求會導致多次對服務的 onStartCommand() 進行相應的呼叫。但是,要停止服務,只需一個服務停止請求(使用 stopSelf()或 stopService())即可。

停止服務

啟動服務必須管理自己的生命週期。也就是說,除非系統必須回收記憶體資源,否則系統不會停止或銷燬服務,而且服務在 onStartCommand()返回後會繼續執行。因此,服務必須通過呼叫 stopSelf()自行停止執行,或者由另一個元件通過呼叫 stopService()來停止它。
一旦請求使用 stopSelf()或 stopService()停止服務,系統就會盡快銷燬服務。
但是,如果服務同時處理多個 onStartCommand()請求,則您不應在處理完一個啟動請求之後停止服務,因為您可能已經收到了新的啟動請求(在第一個請求結束時停止服務會終止第二個請求)。為了避免這一問題,您可以使用 stopSelf(int) 確保服務停止請求始終基於最近的啟動請求。也就說,在呼叫 stopSelf(int)時,傳遞與停止請求的 ID 對應的啟動請求的 ID(傳遞給 onStartCommand()的 startId)。然後,如果在您能夠呼叫 stopSelf(int)之前服務收到了新的啟動請求,ID 就不匹配,服務也就不會停止。

注意:為了避免浪費系統資源和消耗電池電量,應用必須在工作完成之後停止其服務。 如有必要,其他元件可以通過呼叫 stopService()來停止服務。即使為服務啟用了繫結,一旦服務收到對 onStartCommand()的呼叫,您始終仍須親自停止服務。

###建立繫結服務

繫結服務允許應用元件通過呼叫 bindService()與其繫結,以便建立長期連線(通常不允許元件通過呼叫 startService()來啟動它)。
如需與 Activity 和其他應用元件中的服務進行互動,或者需要通過程式間通訊 (IPC) 向其他應用公開某些應用功能,則應建立繫結服務。
要建立繫結服務,必須實現 onBind() 回撥方法以返回 IBinder,用於定義與服務通訊的介面。然後,其他應用元件可以呼叫 bindService()來檢索該介面,並開始對服務呼叫方法。服務只用於與其繫結的應用元件,因此如果沒有元件繫結到服務,則系統會銷燬服務(您不必按通過onStartCommand()啟動的服務那樣來停止繫結服務)。
要建立繫結服務,首先必須定義指定客戶端如何與服務通訊的介面。 服務與客戶端之間的這個介面必須是 IBinder的實現,並且服務必須從onBind() 回撥方法返回它。一旦客戶端收到 IBinder,即可開始通過該介面與服務進行互動。
多個客戶端可以同時繫結到服務。客戶端完成與服務的互動後,會呼叫 unbindService()取消繫結。一旦沒有客戶端繫結到該服務,系統就會銷燬它。
有多種方法實現繫結服務,其實現比啟動服務更為複雜,因此繫結服務將在有關繫結服務的單獨文件中專門討論。

###向使用者傳送通知

一旦執行起來,服務即可使用 Toast 通知或狀態列通知來通知使用者所發生的事件。
Toast 通知是指出現在當前視窗的表面、片刻隨即消失不見的訊息,而狀態列通知則在狀態列中隨訊息一起提供圖示,使用者可以選擇該圖示來採取操作(例如啟動 Activity)。
通常,當某些後臺工作已經完成(例如檔案下載完成)且使用者現在可以對其進行操作時,狀態列通知是最佳方法。 當使用者從展開檢視中選定通知時,通知即可啟動 Activity(例如檢視已下載的檔案)。

###在前臺執行服務

前臺服務被認為是使用者主動意識到的一種服務,因此在記憶體不足時,系統也不會考慮將其終止。 前臺服務必須為狀態列提供通知,放在“正在進行”標題下方,這意味著除非服務停止或從前臺移除,否則不能清除通知。
例如,應該將通過服務播放音樂的音樂播放器設定為在前臺執行,這是因為使用者明確意識到其操作。 狀態列中的通知可能表示正在播放的歌曲,並允許使用者啟動 Activity 來與音樂播放器進行互動。
要請求讓服務執行於前臺,請呼叫 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_ID, notification);複製程式碼

注意:提供給 startForeground()的整型 ID 不得為 0。

要從前臺移除服務,請呼叫 stopForeground()。此方法採用一個布林值,指示是否也移除狀態列通知。 此方法不會停止服務。 但是,如果您在服務正在前臺執行時將其停止,則通知也會被移除。

###管理服務生命週期

服務的生命週期比 Activity 的生命週期要簡單得多。但是,密切關注如何建立和銷燬服務反而更加重要,因為服務可以在使用者沒有意識到的情況下執行於後臺。
服務生命週期(從建立到銷燬)可以遵循兩條不同的路徑:

  • 啟動服務
    該服務在其他元件呼叫 startService()時建立,然後無限期執行,且必須通過呼叫 stopSelf()來自行停止執行。此外,其他元件也可以通過呼叫 stopService()來停止服務。服務停止後,系統會將其銷燬。
  • 繫結服務
    該服務在另一個元件(客戶端)呼叫 bindService()時建立。然後,客戶端通過 IBinder介面與服務進行通訊。客戶端可以通過呼叫unbindService()關閉連線。多個客戶端可以繫結到相同服務,而且當所有繫結全部取消後,系統即會銷燬該服務。 (服務不必自行停止執行。)

這兩條路徑並非完全獨立。也就是說,您可以繫結到已經使用 startService()啟動的服務。例如,可以通過使用 Intent(標識要播放的音樂)呼叫 startService()來啟動後臺音樂服務。隨後,可能在使用者需要稍加控制播放器或獲取有關當前播放歌曲的資訊時,Activity 可以通過呼叫 bindService()繫結到服務。在這種情況下,除非所有客戶端均取消繫結,否則 stopService() 或 stopSelf()不會實際停止服務。
實現生命週期回撥
與 Activity 類似,服務也擁有生命週期回撥方法,您可以實現這些方法來監控服務狀態的變化並適時執行工作。 以下框架服務展示了每種生命週期方法:

public class ExampleService extends Service {    
int mStartMode;       
// indicates how to behave if the service is killed    
IBinder mBinder;      
// interface for clients that bind    
boolean mAllowRebind; 
// indicates whether onRebind should be used    
@Override    
public void onCreate() {        
// The service is being created    
}    
@Override    
public int onStartCommand(Intent intent, int flags, int startId) {        // The service is starting, due to a call to [startService()](https://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent))
        return mStartMode;    
}    
@Override    
public IBinder onBind(Intent intent) {        
// A client is binding to the service with bindService()
        return mBinder;    
}    
@Override    
public boolean onUnbind(Intent intent) {        
// All clients have unbound with unbindService()
        return mAllowRebind;    
}    
@Override    
public void onRebind(Intent intent) {        
// A client is binding to the service with bindService(),        
// after onUnbind() has already been called    
}    
@Override    
public void onDestroy() {        
// The service is no longer used and is being destroyed    
}}複製程式碼

注:與 Activity 生命週期回撥方法不同,您需要呼叫這些回撥方法的超類實現。

四大元件之 Service
圖 2. 服務生命週期。左圖顯示了使用 startService()所建立的服務的生命週期,右圖顯示了使用 bindService()所建立的服務的生命週期。
通過實現這些方法,您可以監控服務生命週期的兩個巢狀迴圈:
服務的整個生命週期從呼叫 onCreate()開始起,到 onDestroy()返回時結束。與 Activity 類似,服務也在 onCreate()中完成初始設定,並在 onDestroy()中釋放所有剩餘資源。例如,音樂播放服務可以在 onCreate(中建立用於播放音樂的執行緒,然後在 onDestroy()中停止該執行緒。無論服務是通過 startService()還是 bindService()建立,都會為所有服務呼叫 onCreate()和 onDestroy()方法。

服務的有效生命週期從呼叫 onStartCommand()或 onBind()方法開始。每種方法均有 Intent物件,該物件分別傳遞到 startService()或 bindService()。對於啟動服務,有效生命週期與整個生命週期同時結束(即便是在 onStartCommand()返回之後,服務仍然處於活動狀態)。對於繫結服務,有效生命週期在 onUnbind()返回時結束。

注:儘管啟動服務是通過呼叫 stopSelf()或 stopService()來停止,但是該服務並無相應的回撥(沒有 onStop()
回撥)。因此,除非服務繫結到客戶端,否則在服務停止時,系統會將其銷燬 — onDestroy()是接收到的唯一回撥。

圖 2 說明了服務的典型回撥方法。儘管該圖分開介紹通過 startService()建立的服務和通過 bindService()建立的服務,但是請記住,不管啟動方式如何,任何服務均有可能允許客戶端與其繫結。因此,最初使用 onStartCommand()(通過客戶端呼叫 startService()啟動的服務仍可接收對 onBind()的呼叫(當客戶端呼叫 bindService()時)。

相關文章