服務總覽

Service 是一種 應用程式元件 持續在背景執行長時間執行的作業不提供使用者介面。一次 服務可能會持續運作一段時間,即便使用者切換至另一項服務也一樣 應用程式。此外,元件也可以繫結至服務來與其互動,甚至執行 處理序間通訊 (IPC)舉例來說,服務可以處理網路交易、播放 音樂、執行檔案 I/O 或與內容供應器互動,作業全都在背景執行。

注意:服務會在代管服務的主要執行緒中執行 程序;服務「不會」建立自己的執行緒,也不會 除非另有指定,否則會透過獨立程序執行。建議您在 在服務中建立個別執行緒,以避免應用程式 無回應 (ANR) 錯誤:

服務類型

下列是三種不同的服務類型:

前景

前景服務會執行某些作業, 內容。舉例來說,音訊應用程式會使用前景服務播放 音軌。前景服務必須顯示通知。 即使使用者未與應用程式互動,前景服務仍會繼續執行 與應用程式互動

使用前景服務時,必須顯示通知, 使用者會主動意識到服務正在執行。這則通知無法 關閉狀態,除非將服務停止或從 前景。

進一步瞭解如何設定 前景服務 應用程式。

注意: WorkManager API 提供靈活的排程工作方式, 能夠執行 將這些工作當做前景服務。在許多情況下 WorkManager 比較適合直接使用前景服務。

背景
背景服務執行了未直接發現的作業 使用者。舉例來說,如果應用程式使用服務來縮減儲存空間 通常會是背景服務

注意:如果應用程式指定的 API 級別為 26 以上,系統會對執行背景作業設下限制 服務。大多數 不同情境 存取位置資訊, 背景運用工具安排工作 WorkManager

已繫結
如果應用程式元件藉由呼叫 bindService() 繫結至該服務,服務就會「繫結」。繫結服務提供用戶端與伺服器 這個介面可讓元件與服務互動、傳送要求、接收結果。 甚至是在不同程序間通訊 (IPC)僅限執行繫結的服務 前提是其他應用程式元件繫結到容器中多個元件可以繫結至 服務,但一旦取消繫結,服務就會遭到刪除。

雖然本說明文件通常會分別討論啟動及繫結服務, 您的服務可雙向運作 — 可啟動 (無限期執行),同時可讓您 繫結。只須實作兩種回呼方法:onStartCommand() 可讓元件啟動,而 onBind() 則允許繫結。

無論您的服務是啟動、繫結,或兩者皆是,任何應用程式元件 這項服務 (甚至是來自另一個應用程式) 的使用方式和任何元件可使用的方式相同 以 Intent 啟動活動。不過,您可以宣告 將服務設為 私人,並封鎖其他應用程式的存取權。 請參閱在 資訊清單

選擇服務和執行緒

服務只是一個可在背景執行的元件 因此,建議您只建立符合您需求的服務 需求。

如果您必須在主執行緒外執行作業,但只能在使用者互動期間 而應改為在其他應用程式的內容中建立新的執行緒 元件。舉例來說,如果想播放音樂,但僅限進行活動時 即可在 onCreate() 中建立討論串 然後在 onStart() 內開始執行 在 onStop()中停止傳送。 也可以考慮使用 java.util.concurrent 套件的執行緒集區和執行工具 或 Kotlin 協同程式 Thread 類別。詳情請參閱 「Android 執行緒作業」文件,進一步說明 將執行作業移至背景執行緒。

請記住,如果您使用了服務,該服務仍會在應用程式的主執行緒中執行, 因此如果服務執行大量作業,您仍應在服務中建立新的執行緒 封鎖作業

基本概念

如要建立 Service,您必須建立 Service 的子類別,或是使用 其他子類別在實作中,您必須覆寫一些回呼方法, 處理服務生命週期的重要層面,並提供可讓元件 繫結至服務。這些是最重要的回呼方法,您應確保 覆寫:

onStartCommand()
當系統要求啟動服務的其他元件 (例如活動) 時,系統會呼叫 startService() 以叫用這個方法。 執行此方法時,服務會啟動,並可在 一直處於背景狀態如果您實作這個方法,必須負責在 並呼叫 stopSelf()stopService() 來完成該工作。如果您只想提供繫結, 您必須實作這個方法
onBind()
當其他元件想與服務繫結時 (例如執行 RPC) 時,系統會呼叫 bindService() 以叫用此方法。 實作這個方法時,您必須提供一個可供用戶端使用的介面 用於傳回 IBinder 來與服務通訊。您必須一律啟用 實作這個方法但如果您不想允許繫結,則應傳回 空值。
onCreate()
系統會在服務 (在呼叫 onStartCommand()onBind())。如果服務已在執行,則此方法 物件。
onDestroy()
當服務已經不再使用並且被刪除時,系統會叫用此方法。 服務應實作此方法來清除所有資源,例如: 或接收器。這是服務最後一次接通的呼叫。

如果元件透過呼叫 startService() (進而呼叫 onStartCommand()) 啟動服務,服務 會繼續執行,直到使用 stopSelf() 或其他項目停止為止 呼叫 stopService() 即可停止該元件

如果元件呼叫 bindService() 用於建立服務,且「未」呼叫 onStartCommand(),服務會執行 前提是該元件設定了繫結當服務取消與所有用戶端的繫結後, 作業就會刪除

只有在記憶體不足時,Android 系統才會停止服務,而且必須復原系統。 提供使用者重要活動的相關資源如果服務繫結至有使用者的活動 就不太可能遭人殺害若是宣告服務是在前景執行,則很少終止。 如果服務啟動且長時間執行,系統會降低其位置 就會出現在背景工作清單中,而且服務變得非常容易 終止作業—如果服務已啟動,您必須設計能夠妥善處理重新啟動作業 由系統執行如果系統終止服務,服務會在資源恢復時立即重新啟動 但這也取決於您從 onStartCommand() 傳回的值。如要進一步瞭解 請參閱處理程序和執行緒中的說明,瞭解系統刪除服務的時機。 文件。

以下各節將說明如何建立 「startService()」和 bindService() 服務方法,以及如何使用 從其他應用程式元件載入這些容器

在資訊清單中宣告服務

您必須在應用程式的 檔案,就和活動和其他元件一樣。

如要宣告服務,請加入 <service> 元素 做為 <application> 的子項 元素。範例如下:

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

請參閱 <service> 元素 參考資料,進一步瞭解如何在資訊清單中宣告服務。

您可以在 <service> 元素中加入其他屬性, 定義各項屬性,例如啟動服務所需的權限,以及 執行服務android:name 屬性是唯一的必要屬性,它會指定服務的類別名稱。更新後 您發布應用程式時,請保留這個名稱,以免發生中斷情形的風險 程式碼,因為必須依賴明確的意圖來啟動或繫結服務 (請參閱網誌文章:Things 無法變更)。

注意:為確保應用程式安全無虞,請務必使用 明確意圖,且未宣告Service 您的服務。使用隱含意圖啟動服務會危害安全,因為您無法 可以確定回應意圖的服務,而且使用者看不到哪一項服務 就可以開始從 Android 5.0 (API 級別 21) 開始,當您呼叫 Android 時,系統會擲回例外狀況 使用隱含意圖的 bindService()

您可以透過下列方式確保只有您的應用程式可以使用服務: 包括 android:exported 屬性並設為 false這可有效防止其他應用程式啟動 這項服務也能滿足您的需求

注意: 使用者可以查看裝置上正在執行的服務。如果客戶看到 當使用者不認得或信任的服務時,就會停止服務。於 為了避免使用者意外停止服務, 加入 android:description敬上 屬性加入 <service>。 元素。在說明部分 簡述服務目的和優點 資源

正在建立已啟動的服務

啟動的服務是指另一個元件藉由呼叫 startService() 啟動,然後呼叫服務的 onStartCommand() 方法。

服務啟動後,生命週期就與 啟動該元件的元件服務可無限期在背景執行 啟動元件的元件會遭到刪除因此,服務應在執行工作時自行停止 來完成作業,或呼叫 stopSelf(),或者其他元件 呼叫 stopService() 即可停止回應。

活動等應用程式元件可透過呼叫 startService() 並傳遞 Intent 來啟動服務 ,並指定服務使用的任何資料。服務 在 onStartCommand() 方法中定義此 Intent

舉例來說,假設活動需要將部分資料儲存至線上資料庫。活動 可藉由傳遞意圖至 startService() 來啟動隨附服務,並傳送要儲存的資料。服務收到 onStartCommand() 中的意圖、連線到網際網路,並執行 資料庫交易交易完成後,服務會自行停止, 已刪除。

注意:服務執行程序與應用程式相同 ,而且預設在該應用程式的主執行緒中。如果您的服務採用 當使用者與來自相同應用程式的活動互動時,執行密集或封鎖作業 就會降低活動效能。避免影響應用程式 在服務中啟動新執行緒。

Service 類別是基本類別 類別。擴充這個類別時,請務必建立新的執行緒 服務就能完成所有工作服務會使用應用程式的主執行緒 ,這可能會降低應用程式執行中活動的效能。

Android 架構也提供 IntentService Service 的子類別,該子類別使用 背景工作執行緒,負責處理所有啟動要求,一次一個要求。使用此類別不 推薦。因為 加入背景執行限制。 此外,自 Android 11 起,這項服務也已淘汰。 JobIntentService 可做為 可以取代與新版 Android 相容的 IntentService

以下各節說明如何實作您的自訂服務,不過,建議您 在大部分用途上,強烈建議改用 WorkManager。請參閱 Android 背景處理作業指南 看看是否有符合您需求的解決方案。

擴充 Service 類別

您可以擴充 Service 類別 處理每個傳入意圖基本實作看起來可能像這樣:

Kotlin

class HelloService : Service() {

    private var serviceLooper: Looper? = null
    private var serviceHandler: ServiceHandler? = null

    // Handler that receives messages from the thread
    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            // 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 (e: InterruptedException) {
                // 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 fun 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("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()

            // Get the HandlerThread's Looper and use it for our Handler
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        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
        serviceHandler?.obtainMessage()?.also { msg ->
            msg.arg1 = startId
            serviceHandler?.sendMessage(msg)
        }

        // If we get killed, after returning from here, restart
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        // We don't provide binding, so return null
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

Java

public class HelloService extends Service {
  private Looper serviceLooper;
  private ServiceHandler serviceHandler;

  // 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 doesn't 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
    serviceLooper = thread.getLooper();
    serviceHandler = new ServiceHandler(serviceLooper);
  }

  @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 = serviceHandler.obtainMessage();
      msg.arg1 = startId;
      serviceHandler.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();
  }
}

範例程式碼會處理 onStartCommand() 中的所有來電 並將工作發布至在背景執行緒執行的 Handler。運作方式與 IntentService 類似,依序處理所有要求。 您可以變更程式碼,在執行緒集區執行工作。舉例來說,如果您想要同時執行多項要求,

請注意,onStartCommand() 方法必須傳回 整數值。這個整數是一個值,說明系統應如何在 觸發系統終止備份的事件傳回值 來自「onStartCommand()」必須是下列其中一項 常數:

START_NOT_STICKY
如果在onStartCommand()返回系統後終止服務,請勿重新建立服務 (除非有待處理狀態) 要提供的意圖這是最安全的做法,可避免在必要的情況下執行服務 以及應用程式只要重新啟動任何未完成的工作即可
START_STICKY
如果系統在 onStartCommand() 傳回後終止服務,請重新建立服務並呼叫 onStartCommand(),但「不要」重新提交最後一個意圖。 而是改為呼叫 onStartCommand(), null 意圖,除非有用來啟動服務的待處理意圖。在此情況下 提供這些意圖這項功能適用於媒體播放器 (或類似服務),而且不支援 執行命令,但會無限期執行並等待工作。
START_REDELIVER_INTENT
如果系統在 onStartCommand() 傳回後終止服務,請重建服務,並使用上次傳送至該服務的意圖呼叫 onStartCommand() 課程中也會快速介紹 Memorystore 這是 Google Cloud 的全代管 Redis 服務系統會依序傳送所有待處理的意圖。適合符合以下條件的服務: 使用者主動執行應該立即恢復的工作,例如下載檔案。

如要進一步瞭解這些傳回值,請參閱連結的參考資料 。

啟動服務

您可以透過活動或其他應用程式元件啟動服務,方法是 傳遞 IntentstartService()startForegroundService()。 Android 系統會呼叫服務的 onStartCommand() 方法,並將 Intent 傳遞至該服務。 指定要啟動的服務

注意:如果應用程式指定的 API 級別為 26 以上,系統則會 對於使用或建立背景服務的行為設有限制,除非應用程式可以使用 就在前景運作如果應用程式需要建立前景服務, 應用程式應呼叫 startForegroundService()。該方法會建立背景服務,但 方法會向系統發出信號,告知服務將自行升級為 前景。服務建立完成後,服務必須呼叫 以下範圍有 startForeground() 種方式: 五秒鐘

舉例來說,活動可以使用帶有 startService() 的明確意圖,啟動上一節 (HelloService) 中的範例服務,如下所示:

Kotlin

startService(Intent(this, HelloService::class.java))

Java

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

startService() 方法會立即傳回,且 Android 系統會呼叫服務的 onStartCommand() 方法。如果服務尚未執行,系統會先呼叫 onCreate(),然後再呼叫 onStartCommand()

如果服務也未提供繫結,則透過 startService() 傳送的意圖是兩者之間唯一的通訊模式 應用程式元件和服務不過,如要讓服務傳回結果 啟動服務的用戶端可以為廣播建立 PendingIntent (透過 getBroadcast()) 並提供給服務 在啟動服務的 Intent 中。接著,服務可以使用 傳遞結果

多個啟動服務的要求會導致對服務的 onStartCommand()。不過,只有一個要求可停止 必須透過 stopSelf()stopService() 停止服務。

停止服務

啟動的服務必須管理自己的生命週期。也就是說,系統不會停止 除非必須復原系統記憶體和服務 會繼續執行 onStartCommand() 服務必須藉由呼叫 stopSelf() 或其他方式自行停止 呼叫 stopService() 即可停止元件

要求使用 stopSelf()stopService() 停止執行後,系統會立刻刪除服務

如果您的服務同時處理多項傳送至 onStartCommand() 的要求,請勿停止 服務,因為您可能會收到新的 (在第一個要求結束時停止,就會終止第二個要求)。為了避免 使用 stopSelf(int) 即可確保針對 停止服務一律會根據最近的啟動要求做出。也就是說,當您呼叫 stopSelf(int) 時,會傳遞啟動要求的 ID (startId 傳送至 onStartCommand()),並接收到停止要求。 對應。然後,如果服務在您呼叫 stopSelf(int) 前收到新的啟動要求,就會發現 ID 不相符,服務也不會停止。

注意:為了避免浪費系統資源和資訊 電池電力,請確保應用程式在執行工作時停止其服務。 如有需要,其他元件可以呼叫 stopService() 來停止服務。即使您已為服務啟用繫結 如果服務收到 onStartCommand() 的呼叫,您必須自行停止服務。

如要進一步瞭解服務的生命週期,請參閱下方「管理服務的生命週期」一節。

建立繫結服務

繫結服務可讓應用程式元件透過呼叫 bindService() 建立長期連線,繫結至該服務。 通常不允許元件透過呼叫 startService() 來「啟動」

當您想透過活動與服務互動時,請建立繫結的服務 也能將應用程式的功能提供給使用者 以便對其他應用程式進行處理。

如要建立繫結服務,請實作 onBind() 回呼方法以傳回 IBinder 會定義與服務通訊的介面。接著,其他應用程式元件 bindService():擷取介面並 開始呼叫服務上的方法。服務只能為符合下列條件的應用程式元件提供服務 繫結至服務中,因此如果沒有繫結的元件,系統就會將其銷毀。 您「不」需要以服務在服務終止時 透過 onStartCommand() 開始。

如要建立繫結服務,您必須定義介面,指定用戶端 與服務進行通訊服務之間 用戶端必須實作 IBinder,且您的服務必須 來自 onBind() 回呼方法。用戶端收到 IBinder 後,即可啟動 透過介面與服務互動

多個用戶端可以同時繫結至服務。用戶端完成與 服務就會呼叫 unbindService() 以取消繫結。 如果沒有用戶端繫結至服務,系統會銷毀服務。

實作繫結服務的方法有很多種,實作方式 變得比啟動服務複雜因此,繫結服務討論會顯示在 繫結服務相關文件。

傳送通知給使用者

服務執行期間,您可以透過 Snackbar 通知狀態列通知將事件通知使用者。

Snackbar 通知是訊息,只會出現在目前視窗的表面, 刪除之前狀態列通知會在狀態列中提供一個圖示, 訊息,使用者選取這個訊息,以執行特定動作 (例如啟動活動)。

通常,狀態列通知是背景工作 (例如 檔案下載完成後,使用者即可執行操作。當使用者 從展開的檢視畫面中選取通知,通知便可啟動活動。 (例如顯示下載的檔案)。

管理服務的生命週期

服務的生命週期比活動的生命週期簡單許多。不過 請務必密切留意服務的建立與刪除方式 服務可以在使用者不知情的情況下在背景執行。

從建立時間到刪除服務的時間,服務生命週期可以依循 下列任一路徑:

  • 已啟用的服務

    當其他元件呼叫 startService() 時,就會建立服務。接著,服務會無限期運作,且必須 呼叫 stopSelf()。另一個元件也可達到 呼叫 stopService()。當服務停止時,系統會刪除服務。

  • 繫結服務

    當其他元件 (用戶端) 呼叫 bindService() 時,就會建立服務。接著,用戶端會與服務通訊 經由 IBinder 介面取得。用戶端可以透過呼叫 unbindService()。多個用戶端可以繫結至 相同的服務與所有元件解除繫結時,系統就會銷毀該服務。「服務」 「不需」自行停止。

這兩種路徑並不是完全獨立的路徑,您可以繫結至已經在使用的服務 開始時間:startService()。舉例來說: 使用用於識別要播放的音樂的 Intent 呼叫 startService(),即可啟動背景音樂服務。之後 或想取得玩家控制權或 目前歌曲,則只要呼叫 bindService(),即可將活動繫結至服務。在這類情況下,stopService()stopSelf() 實際上不會停止服務,直到所有用戶端解除繫結為止。

實作生命週期回呼

就像活動一樣,服務具有生命週期回呼方法,您可以實作以監控 並在適當時間執行作業。以下骷髏 Service 會示範每個生命週期方法:

Kotlin

class ExampleService : Service() {
    private var startMode: Int = 0             // indicates how to behave if the service is killed
    private var binder: IBinder? = null        // interface for clients that bind
    private var allowRebind: Boolean = false   // indicates whether onRebind should be used

    override fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Java

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // 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()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @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
    }
}

注意:與活動生命週期回呼方法不同,您必須 「不需要」呼叫這些回呼方法的父類別實作。

圖 2. 服務生命週期。左側圖表 使用 startService() 建立服務的生命週期,右圖顯示服務建立時的生命週期 bindService()

圖 2 說明服務的典型回呼方法。雖然模型彼此區隔 這些免費服務startService() 由「bindService()」建立,請保留 請記得,無論如何啟動服務,都可能允許用戶端繫結。 一開始透過 onStartCommand() 啟動的服務 (由用戶端呼叫 startService()) 仍可接收對 onBind() 的呼叫 (用戶端呼叫時) bindService())。

實作這些方法後,您就能監控服務的兩個巢狀迴圈 生命週期:

注意:雖然呼叫 stopSelf()stopService(),則沒有 服務 (沒有 onStop() 回呼)。除非服務已繫結至用戶端,否則 並在服務停止時刪除它;onDestroy() 是唯一收到的回呼。

如要進一步瞭解如何建立提供繫結的服務,請參閱「繫結服務」文件。 內含《onRebind()》 回呼方法,請參閱「管理 繫結服務