Uruchamianie usługi na pierwszym planie

Uruchomienie usługi działającej na pierwszym planie z aplikacji wymaga wykonania 2 czynności. Najpierw musisz uruchomić usługę, wywołując metodę context.startForegroundService(). Następnie wywołaj ServiceCompat.startForeground(), aby usługa została przeniesiona na pierwszy plan.

Wymagania wstępne

W zależności od poziomu interfejsu API, na który jest kierowana aplikacja, obowiązują pewne ograniczenia dotyczące tego, kiedy aplikacja może uruchomić usługę na pierwszym planie.

  • Aplikacje kierowane na Androida 12 (interfejs API na poziomie 31) lub nowszego nie mogą uruchamiać usługi działającej na pierwszym planie, gdy aplikacja działa w tle, z kilkoma wyjątkami. Więcej informacji, w tym o wyjątkach od tej reguły, znajdziesz w artykule Ograniczenia dotyczące uruchamiania usługi działającej na pierwszym planie z poziomu tła.

  • Aplikacje kierowane na Androida 14 (API na poziomie 34) lub nowszego muszą prosić o odpowiednie uprawnienia dla typu usługi działającej na pierwszym planie. Gdy aplikacja próbuje przenieść usługę na pierwszy plan, system sprawdza odpowiednie uprawnienia i wyświetla błąd SecurityException, jeśli aplikacji brakuje któregoś z nich. Jeśli na przykład spróbujesz uruchomić usługę na pierwszym planie typu location, system sprawdzi, czy aplikacja ma już uprawnienie ACCESS_COARSE_LOCATION lub ACCESS_FINE_LOCATION. W dokumentacji typu usługi na pierwszym planie znajdziesz wymagania wstępne dotyczące każdego typu usługi na pierwszym planie.

Uruchamianie usługi

Aby uruchomić usługę na pierwszym planie, musisz najpierw uruchomić ją jako zwykłą usługę (nie na pierwszym planie):

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);

Najważniejsze informacje o kodzie

  • Fragment kodu uruchamia usługę. Jednak usługa nie działa jeszcze na pierwszym planie. W samej usłudze musisz wywołać ServiceCompat.startForeground(), aby przekształcić ją w usługę na pierwszym planie.

Przenoszenie usługi na pierwszy plan

Gdy usługa jest uruchomiona, musisz wywołać metodę ServiceCompat.startForeground(), aby poprosić o jej uruchomienie na pierwszym planie. Zwykle wywołuje się tę metodę w metodzie onStartCommand() usługi.

ServiceCompat.startForeground() przyjmuje te parametry:

Typy usług na pierwszym planie, które przekazujesz do metody startForeground(), typy zadeklarowane w pliku manifestu, w zależności od konkretnego przypadku użycia. Jeśli chcesz dodać więcej typów usług, możesz ponownie wywołać metodę startForeground().

Załóżmy na przykład, że aplikacja treningowa uruchamia usługę śledzenia biegu, która zawsze potrzebuje informacji location, ale może, ale nie musi, odtwarzać multimedia. W pliku manifestu musisz zadeklarować zarówno location, jak i mediaPlayback. Jeśli użytkownik rozpoczyna bieg i chce tylko, aby jego lokalizacja była śledzona, aplikacja powinna wywołać startForeground() i przekazać tylko uprawnienie ACCESS_FINE_LOCATION. Jeśli użytkownik chce rozpocząć odtwarzanie dźwięku, ponownie wywołaj startForeground() i przekaż kombinację bitową wszystkich typów usług na pierwszym planie (w tym przypadku ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK).

Poniższy przykład pokazuje kod, którego usługa aparatu użyłaby do przekształcenia się w usługę na pierwszym planie:

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)
            }
            // ...
        }
    }

    //...
}

Najważniejsze informacje o kodzie

  • Aplikacja zadeklarowała już w pliku manifestu, że potrzebuje uprawnienia CAMERA. Jednak aplikacja musi też sprawdzać w czasie działania, czy użytkownik przyznał jej to uprawnienie. Jeśli aplikacja nie ma odpowiednich uprawnień, powinna poinformować użytkownika o problemie.
  • Różne typy usług na pierwszym planie zostały wprowadzone w różnych wersjach platformy Androida. Ten kod sprawdza, na jakiej wersji Androida działa, i prosi o odpowiednie uprawnienia.
  • Kod sprawdza, czy nie występuje błąd ForegroundServiceStartNotAllowedException, gdy próbuje uruchomić usługę na pierwszym planie w niedozwolonej sytuacji (np. gdy próbuje przenieść usługę na pierwszy plan, gdy aplikacja działa w tle).