Usługi działające na pierwszym planie

Usługi działające na pierwszym planie wykonują operacje, które są widoczne dla użytkownika.

Usługi na pierwszym planie wyświetlają powiadomienie na pasku stanu, aby poinformować użytkowników, że aplikacja wykonuje zadanie na pierwszym planie i zużywa zasoby systemowe.

Przykłady aplikacji korzystających z usług na pierwszym planie:

  • Aplikacja odtwarzacza muzyki, która odtwarza muzykę w usłudze na pierwszym planie. Powiadomienie może zawierać aktualnie odtwarzany utwór.
  • Aplikacja do fitnessu, która rejestruje działanie użytkownika w usłudze na pierwszym planie po uzyskaniu od niego zgody. Powiadomienie może zawierać pokonany dystans podczas bieżącej sesji fitness.

Korzystaj z usługi na pierwszym planie tylko wtedy, gdy aplikacja musi wykonać zadanie widoczne dla użytkownika, nawet jeśli nie wchodzi on z nią w bezpośrednią interakcję. Jeśli działanie jest na tyle mało ważne, że chcesz używać powiadomienia o minimalnym priorytecie, utwórz zadanie w tle.

W tym dokumencie opisujemy, jakie uprawnienia są wymagane do korzystania z usług działających na pierwszym planie oraz jak uruchomić taką usługę i usunąć ją z tle. Wyjaśniamy również, jak powiązać określone przypadki użycia z typami usług działających na pierwszym planie oraz o ograniczeniach dostępu obowiązujących po uruchomieniu takiej usługi z poziomu aplikacji, która działa w tle.

Użytkownik może domyślnie zamknąć powiadomienie

Począwszy od Androida 13 (poziom interfejsu API 33) użytkownicy mogą domyślnie odrzucać powiadomienia powiązane z usługami na pierwszym planie. Aby to zrobić, użytkownik musi wykonać gest przesuwania na powiadomieniu. Zazwyczaj powiadomienie nie jest zamykane, chyba że usługa działająca na pierwszym planie zostanie zatrzymana lub z niej usunięta.

Jeśli chcesz, aby użytkownik nie mógł zamknąć powiadomienia, przekaż true do metody setOngoing() podczas tworzenia powiadomienia za pomocą Notification.Builder.

Usługi, które wyświetlają powiadomienie natychmiast

Jeśli usługa działająca na pierwszym planie ma co najmniej 1 z tych cech, system wyświetla powiązane powiadomienie natychmiast po jej uruchomieniu, nawet na urządzeniach z Androidem 12 lub nowszym:

Jeśli użytkownik odmówi przyznania uprawnień na powiadomienia na Androidzie 13 (poziom interfejsu API 33) lub nowszym, w Menedżerze zadań będzie nadal widzieć powiadomienia związane z usługami działającymi na pierwszym planie, ale nie będą widoczne w panelu powiadomień.

Deklarowanie usług działających na pierwszym planie w pliku manifestu

W pliku manifestu aplikacji zadeklaruj każdą usługę działającą na pierwszym planie za pomocą elementu <service>. W przypadku każdej usługi użyj atrybutu android:foregroundServiceType, aby zadeklarować rodzaj wykonywanej pracy.

Jeśli na przykład aplikacja tworzy usługę na pierwszym planie, która odtwarza muzykę, możesz zadeklarować tę usługę w ten sposób:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
  <application ...>

    <service
        android:name=".MyMediaPlaybackService"
        android:foregroundServiceType="mediaPlayback"
        android:exported="false">
    </service>
  </application>
</manifest>

Jeśli do Twojej usługi ma zastosowanie kilka typów, rozdziel je za pomocą operatora |. Na przykład usługa korzystająca z kamery i mikrofonu powinna zadeklarować to w ten sposób:

android:foregroundServiceType="camera|microphone"

Wysyłanie prośby o uprawnienia usługi na pierwszym planie

Aplikacje kierowane na Androida 9 (poziom interfejsu API 28) lub nowszego i korzystające z usług działających na pierwszym planie muszą żądać parametru FOREGROUND_SERVICE w manifeście aplikacji, jak pokazano w tym fragmencie kodu. Jest to normalne uprawnienie, więc system automatycznie przyznaje je aplikacji, która wysłała żądanie.

Dodatkowo, jeśli aplikacja jest kierowana na interfejs API na poziomie 34 lub wyższym, musi żądać odpowiedniego typu uprawnień do rodzaju pracy wykonywanej przez usługę na pierwszym planie. Każdy typ usługi na pierwszym planie ma odpowiedni typ uprawnień. Jeśli na przykład aplikacja uruchamia usługę na pierwszym planie, która korzysta z kamery, musisz poprosić o uprawnienia FOREGROUND_SERVICE i FOREGROUND_SERVICE_CAMERA. To są wszystkie normalne uprawnienia, więc system przyznaje je automatycznie, jeśli są wymienione w pliku manifestu.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>

    <application ...>
        ...
    </application>
</manifest>

Wymagania wstępne usługi działającej na pierwszym planie

Począwszy od Androida 14 (poziom interfejsu API 34), po uruchomieniu usługi na pierwszym planie system sprawdza określone wymagania wstępne w zależności od typu usługi. Jeśli na przykład spróbujesz uruchomić usługę działającą na pierwszym planie typu location, system sprawdzi, czy aplikacja ma już uprawnienie ACCESS_COARSE_LOCATION lub ACCESS_FINE_LOCATION. Jeśli go nie ma, system zgłasza SecurityException.

Z tego powodu przed uruchomieniem usługi na pierwszym planie musisz potwierdzić, że są spełnione wymagane wymagania wstępne. Dokumentacja typu usługi na pierwszym planie zawiera listę wymagań wstępnych dla każdego typu usługi na pierwszym planie.

Uruchom usługę na pierwszym planie

Zanim poprosisz system o uruchomienie usługi jako usługi na pierwszym planie, uruchom samą usługę:

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

W usłudze, zwykle w trybie onStartCommand(), możesz poprosić o jej uruchomienie na pierwszym planie. Aby to zrobić, wywołaj ServiceCompat.startForeground() (dostępny w Androidziex Core 1.12 i nowszych). Przyjmuje ona te parametry:

Te typy mogą być podzbiorem typów zadeklarowanych w pliku manifestu w zależności od konkretnego przypadku użycia. Następnie, jeśli chcesz dodać więcej typów usług, możesz ponownie wywołać startForeground().

Załóżmy na przykład, że aplikacja do fitnessu korzysta z usługi trackera biegowego, która zawsze potrzebuje informacji z location, ale nie musi odtwarzać multimediów. W pliku manifestu musisz zadeklarować zarówno location, jak i mediaPlayback. Jeśli użytkownik rozpocznie uruchomienie i chce tylko śledzić swoją lokalizację, aplikacja powinna wywołać metodę startForeground() i przekazać tylko uprawnienie ACCESS_FINE_LOCATION. Następnie, jeśli użytkownik chce zacząć odtwarzać dźwięk, wywołaj ponownie startForeground() i przekaż bitową kombinację wszystkich typów usług na pierwszym planie (w tym przypadku ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK).

Oto przykład, który uruchamia usługę kamery 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)
            }
            // ...
        }
    }

    //...
}

Usuwanie usługi z pierwszego planu

Aby usunąć usługę z pierwszego planu, wywołaj stopForeground(). Ta metoda przyjmuje wartość logiczną, która wskazuje, czy usunąć też powiadomienie na pasku stanu. Pamiętaj, że usługa będzie nadal działać.

Jeśli zatrzymasz usługę, gdy działa na pierwszym planie, powiadomienie o niej zostanie usunięte.

Obsługuj zainicjowane przez użytkownika zatrzymywanie aplikacji działających na pierwszym planie

U dołu panelu powiadomień znajduje się przycisk wskazujący liczbę aplikacji działających obecnie w tle. Gdy naciśniesz ten przycisk, pojawi się okno z listą nazw różnych aplikacji. Przycisk Zatrzymaj jest po prawej stronie każdej aplikacji.
Rysunek 1. Przepływ pracy w Menedżerze zadań na urządzeniach z Androidem 13 lub nowszym.

Począwszy od Androida 13 (poziom interfejsu API 33) użytkownicy mogą wykonać przepływ pracy z panelu powiadomień, aby zatrzymać aplikację z aktywnymi usługami na pierwszym planie, niezależnie od docelowej wersji pakietu SDK tej aplikacji. Ta afordancja, nazywana Menedżerem zadań, wyświetla listę aplikacji, które działają obecnie na pierwszym planie.

Ta lista jest oznaczona jako Aktywne aplikacje. Obok każdej aplikacji znajduje się przycisk Zatrzymaj. Rysunek 1 przedstawia przepływ pracy w Menedżerze zadań na urządzeniu z Androidem 13.

Gdy użytkownik naciśnie w Menedżerze zadań przycisk Zatrzymaj obok aplikacji, zostaną wykonane te czynności:

  • System usunie aplikację z pamięci. W związku z tym cała aplikacja zostaje zatrzymana, a nie tylko uruchomiona usługa na pierwszym planie.
  • System usunie stos aktywności w aplikacji.
  • Odtwarzanie multimediów zostanie zatrzymane.
  • Powiadomienie powiązane z usługą na pierwszym planie zostanie usunięte.
  • Aplikacja pozostaje w historii.
  • Zaplanowane zadania są wykonywane o zaplanowanej godzinie.
  • Alarmy dzwonią o zaplanowanej godzinie lub w przedziale czasowym.

Aby sprawdzić, czy aplikacja działa zgodnie z oczekiwaniami, gdy użytkownik ją zatrzymuje i po jej zatrzymaniu, uruchom to polecenie ADB w oknie terminala:

adb shell cmd activity stop-app PACKAGE_NAME

Zwolnienia

W przypadku określonych typów aplikacji system zapewnia kilka poziomów odstąpień od tej reguły, które zostały opisane w sekcjach poniżej.

Wykluczenia dotyczą poszczególnych aplikacji, a nie procesów. Jeśli system wykluczy jeden proces w aplikacji, wszystkie pozostałe procesy w tej aplikacji również zostaną wykluczone.

wykluczenia z wyświetlania w Menedżerze zadań.

Te aplikacje mogą działać jako usługi na pierwszym planie i w ogóle nie pojawiać się w Menedżerze zadań:

Wykluczenia, których użytkownicy mogą nie zatrzymać

Gdy na pierwszym planie działają te typy aplikacji, pojawiają się w Menedżerze zadań, ale obok nazwy aplikacji nie ma przycisku Zatrzymaj, który użytkownik może kliknąć:

Używaj przeznaczonych do tego interfejsów API zamiast usług działających na pierwszym planie

W wielu przypadkach istnieją interfejsy API platformy lub Jetpack, których można użyć do pracy, do której można by wykorzystać usługę na pierwszym planie. Jeśli istnieje odpowiedni interfejs API spełniający swoje zadanie, niemal zawsze należy go używać zamiast usługi na pierwszym planie. Specjalistyczne interfejsy API często dają dodatkowe możliwości specyficzne dla danego przypadku użycia, które w innym przypadku należałoby opracować samodzielnie. Na przykład interfejs Bubbles API obsługuje złożoną logikę UI w aplikacjach do obsługi wiadomości, które muszą zaimplementować funkcje dymków czatu.

Dokumentacja typów usług działających na pierwszym planie zawiera listę dobrych alternatywnych rozwiązań.

Ograniczenia dotyczące uruchamiania usług na pierwszym planie w tle

Aplikacje kierowane na Androida 12 lub nowszego nie mogą uruchamiać usług na pierwszym planie, gdy działają w tle. Wyjątkiem są rzadkie przypadki. Jeśli aplikacja próbuje uruchomić usługę na pierwszym planie, gdy działa w tle, a taka działa w jednym z wyjątkowych przypadków, system zgłasza ForegroundServiceStartNotAllowedException.

Ponadto jeśli aplikacja chce uruchomić usługę działającą na pierwszym planie, która wymaga uprawnień podczas używania (np. uprawnień dotyczących czujnika na ciele, aparatu, mikrofonu czy lokalizacji), nie może utworzyć tej usługi, gdy aplikacja działa w tle, nawet jeśli podlega jednym z wykluczeń z ograniczeń uruchamiania w tle. Wyjaśnienie zostało opisane w sekcji Ograniczenia uruchamiania usług działających na pierwszym planie, które wymagają uprawnień podczas używania.

Wykluczenia z ograniczeń uruchamiania w tle

W tych sytuacjach aplikacja może uruchamiać usługi na pierwszym planie nawet wtedy, gdy działa w tle:

Ograniczenia dotyczące uruchamiania usług działających na pierwszym planie, które wymagają uprawnień podczas używania

W Androidzie 14 (poziom interfejsu API 34) lub nowszym należy wziąć pod uwagę szczególne sytuacje, jeśli uruchamiasz usługę na pierwszym planie, która wymaga uprawnień podczas używania.

Jeśli Twoja aplikacja jest kierowana na Androida 14 lub nowszego, system operacyjny sprawdza podczas tworzenia usługi działającej na pierwszym planie, czy aplikacja ma wszystkie odpowiednie uprawnienia dla danego typu usługi. Gdy na przykład tworzysz usługę na pierwszym planie typu mikrofon, system operacyjny sprawdza, czy aplikacja ma obecnie uprawnienia RECORD_AUDIO. Jeśli nie masz tych uprawnień, system zgłasza SecurityException.

W przypadku uprawnień podczas używania może to powodować problem. Jeśli aplikacja ma uprawnienie podczas używania, ma je tylko na pierwszym planie. Oznacza to, że jeśli aplikacja działa w tle i próbuje utworzyć usługę działającą na pierwszym planie typu aparat, lokalizację lub mikrofon, system zauważy, że aplikacja obecnie nie ma wymaganych uprawnień i zwraca SecurityException.

Podobnie, jeśli aplikacja działa w tle i tworzy usługę ochrony zdrowia, która wymaga uprawnienia BODY_SENSORS_BACKGROUND, aplikacja nie ma obecnie tego uprawnienia, a system zgłasza wyjątek. (Nie dotyczy to placówek ochrony zdrowia, które wymagają różnych uprawnień, np. ACTIVITY_RECOGNITION). Połączenie PermissionChecker.checkSelfPermission() nie zapobiega temu problemowi. Jeśli Twoja aplikacja ma uprawnienie podczas używania i wywołuje metodę checkSelfPermission(), by sprawdzić, czy ma to uprawnienie, metoda zwraca PERMISSION_GRANTED, nawet jeśli aplikacja działa w tle. Gdy metoda zwraca PERMISSION_GRANTED, wyświetla się komunikat „Twoja aplikacja ma to uprawnienie, gdy jest używana”.

Z tego powodu, jeśli usługa na pierwszym planie wymaga uprawnień podczas używania, musisz wywołać metodę Context.startForegroundService() lub Context.bindService(), gdy w aplikacji występuje widoczna aktywność, chyba że usługa należy do jednego ze zdefiniowanych wyjątków.

Wykluczenia z ograniczeń dotyczących uprawnień podczas użycia

W niektórych sytuacjach, nawet jeśli usługa działająca na pierwszym planie, gdy aplikacja działa w tle, ma dostęp do informacji o lokalizacji, aparacie i mikrofonie, gdy aplikacja działa na pierwszym planie („podczas używania”).

W tych samych sytuacjach, jeśli usługa zadeklaruje typ usługi na pierwszym planie o wartości location i zostanie uruchomiona przez aplikację z uprawnieniami ACCESS_BACKGROUND_LOCATION, usługa ta może uzyskiwać dostęp do informacji o lokalizacji przez cały czas, nawet gdy aplikacja działa w tle.

Oto lista sytuacji:

  • Komponent systemu uruchamia usługę.
  • Uruchamianie usługi wymaga interakcji z widżetami aplikacji.
  • Działanie usługi rozpoczyna się od interakcji z powiadomieniem.
  • Usługa uruchamia się jako obiekt PendingIntent wysyłany z innej, widocznej aplikacji.
  • Uruchamia się aplikacja, która jest kontrolerem zasad dotyczących urządzeń, która działa w trybie właściciela urządzenia.
  • Usługa jest uruchamiana przez aplikację, która udostępnia interfejs VoiceInteractionService.
  • Usługa jest uruchamiana przez aplikację, która ma uprawnienie START_ACTIVITIES_FROM_BACKGROUND.
Określ, których usług dotyczy Twoja aplikacja

Podczas testowania aplikacji uruchom jej usługi na pierwszym planie. Jeśli uruchomiona usługa ograniczyła dostęp do lokalizacji, mikrofonu i kamery, w logcat pojawi się ten komunikat:

Foreground service started from background can not have \
location/camera/microphone access: service SERVICE_NAME