啟動前景服務

如要從應用程式啟動前景服務,請按照下列兩個步驟操作。首先,您必須呼叫 context.startForegroundService() 啟動服務。接著,讓服務呼叫 ServiceCompat.startForeground(),將自身升級為前景服務。

必要條件

應用程式啟動前景服務的時間會受到限制,實際情況視應用程式指定的 API 級別而定。

  • 如果應用程式指定 Android 12 (API 級別 31) 以上版本,就不得在應用程式於背景執行時啟動前景服務 (少數特定例外情況除外)。如要瞭解詳情,以及這項規則的例外情形,請參閱「從背景啟動前景服務的限制」。

  • 指定 Android 14 (API 級別 34) 以上版本的應用程式,必須為前景服務類型要求適當的權限。當應用程式嘗試將服務升級為前景服務時,系統會檢查適當的權限,如果應用程式缺少任何權限,就會擲回 SecurityException。舉例來說,如果您嘗試啟動 location 類型的前景服務,系統會檢查應用程式是否已具備 ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION 權限。前景服務類型文件列出了每種前景服務類型所需的必要條件。

啟動服務

如要啟動前景服務,您必須先將其啟動為一般 (非前景) 服務:

Kotlin

val intent = Intent(...) // Build the intent for the service
context.startForegroundService(intent)

Java

Context context = getApplicationContext();
Intent intent = new Intent(...); // Build the intent for the service
context.startForegroundService(intent);

程式碼重點

  • 程式碼片段會啟動服務。不過,這項服務尚未在前台執行。在服務本身中,您需要呼叫 ServiceCompat.startForeground(),將服務升級為前景服務。

將服務升級為前景服務

服務執行後,您需要呼叫 ServiceCompat.startForeground(),要求服務在前台執行。通常您會在服務的 onStartCommand() 方法中呼叫這個方法。

ServiceCompat.startForeground() 會使用下列參數:

  • 這項服務。
  • 正整數,用於在狀態列中唯一識別服務的通知。
  • Notification 物件本身。
  • 前景服務類型,用來識別服務執行的工作

視具體用途而定,您傳遞至 startForeground() 資訊清單中聲明的類型的前景服務類型。接著,如需新增更多服務類型,可以再次呼叫 startForeground()

舉例來說,假設健身應用程式執行跑步追蹤器服務,一律需要 location 資訊,但可能需要或不需要播放媒體。您需要在資訊清單中同時宣告 locationmediaPlayback。如果使用者開始跑步,只是想追蹤自己的位置,應用程式應呼叫 startForeground(),並只傳遞 ACCESS_FINE_LOCATION 權限。接著,如果使用者想開始播放音訊,請再次呼叫 startForeground(),並傳遞所有前景服務類型的位元組合 (在本例中為 ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK)。

以下範例顯示相機服務用來將自身升級為前景服務的程式碼:

Kotlin

class MyCameraService: Service() {

  private fun startForeground() {
    // Before starting the service as foreground check that the app has the
    // appropriate runtime permissions. In this case, verify that the user has
    // granted the CAMERA permission.
    val cameraPermission =
            PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA)
    if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) {
        // Without camera permissions the service cannot run in the foreground
        // Consider informing user or updating your app UI if visible.
        stopSelf()
        return
    }

    try {
        val notification = NotificationCompat.Builder(this, "CHANNEL_ID")
            // Create the notification to display while the service is running
            .build()
        ServiceCompat.startForeground(
            /* service = */ this,
            /* id = */ 100, // Cannot be 0
            /* notification = */ notification,
            /* foregroundServiceType = */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
            } else {
                0
            },
        )
    } catch (e: Exception) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                && e is ForegroundServiceStartNotAllowedException) {
            // App not in a valid state to start foreground service
            // (e.g. started from bg)
        }
        // ...
    }
  }
}

Java

public class MyCameraService extends Service {

    private void startForeground() {
        // Before starting the service as foreground check that the app has the
        // appropriate runtime permissions. In this case, verify that the user
        // has granted the CAMERA permission.
        int cameraPermission =
            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (cameraPermission == PackageManager.PERMISSION_DENIED) {
            // Without camera permissions the service cannot run in the
            // foreground. Consider informing user or updating your app UI if
            // visible.
            stopSelf();
            return;
        }

        try {
            Notification notification =
                new NotificationCompat.Builder(this, "CHANNEL_ID")
                    // Create the notification to display while the service
                    // is running
                    .build();
            int type = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
            }
            ServiceCompat.startForeground(
                    /* service = */ this,
                    /* id = */ 100, // Cannot be 0
                    /* notification = */ notification,
                    /* foregroundServiceType = */ type
            );
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
                    e instanceof ForegroundServiceStartNotAllowedException
            ) {
                // App not in a valid state to start foreground service
                // (e.g started from bg)
            }
            // ...
        }
    }

    //...
}

程式碼重點

  • 應用程式已在資訊清單中宣告需要 CAMERA 權限。不過,應用程式也必須在執行階段檢查,確保使用者已授予該項權限。如果應用程式實際上沒有正確的權限,應告知使用者問題所在。
  • 不同版本的 Android 平台會導入不同的前景服務類型。這段程式碼會檢查目前執行的 Android 版本,並要求適當的權限。
  • 程式碼會檢查 ForegroundServiceStartNotAllowedException,以免在不允許的情況下嘗試啟動前景服務 (例如,嘗試在應用程式處於背景狀態時將服務升級為前景服務)。