Przegląd usług

Service to komponent aplikacji, który może wykonywać długotrwałe operacje w tle. Nie ma interfejsu. Uruchomiona usługa może działać jeszcze przez jakiś czas, nawet gdy użytkownik przełączy się na inną aplikację. Dodatkowo komponent może powiązać się z usługą, aby wchodzić z nią w interakcję, a nawet wykonywać komunikację międzyprocesową (IPC). Na przykład usługa może obsługiwać transakcje sieciowe, odtwarzać muzykę, wykonywać operacje wejścia-wyjścia plików czy wchodzić w interakcje z dostawcą treści – a wszystko to w tle.

Uwaga: usługa działa w głównym wątku swojego procesu hostingu. Nie tworzy własnego wątku ani nie uruchamia się w osobnym procesie, chyba że określisz inaczej. Aby uniknąć błędów ANR (Application Not Responding), należy uruchamiać operacje blokujące w oddzielnym wątku w usłudze.

Rodzaje usług

Są to 3 rodzaje usług:

Pierwszy plan

Usługa na pierwszym planie wykonuje pewne działanie, które jest widoczne dla użytkownika. Na przykład aplikacja audio będzie używać usługi na pierwszym planie do odtwarzania ścieżki audio. Usługi działające na pierwszym planie muszą wyświetlać powiadomienie. Usługi na pierwszym planie działają nawet wtedy, gdy użytkownik nie korzysta z aplikacji.

Jeśli używasz usługi na pierwszym planie, musisz wyświetlić powiadomienie, aby użytkownicy wiedzieli, że usługa jest uruchomiona. Tego powiadomienia nie można zamknąć, dopóki usługa nie zostanie zatrzymana lub usunięta z pierwszego planu.

Dowiedz się więcej o konfigurowaniu usług na pierwszym planie w aplikacji.

Uwaga: interfejs API WorkManager zapewnia elastyczny sposób planowania zadań i w razie potrzeby może uruchamiać te zadania jako usługi na pierwszym planie. W wielu przypadkach lepiej jest użyć WorkManagera niż bezpośrednio używać usług działających na pierwszym planie.

Tło
Usługa w tle wykonuje operację, której użytkownik nie zauważy bezpośrednio. Jeśli na przykład aplikacja korzystała z usługi w celu zmniejszenia rozmiaru pamięci, zwykle jest to usługa w tle.

Uwaga: jeśli aplikacja jest kierowana na interfejs API na poziomie 26 lub wyższym, system nakłada ograniczenia na działanie usług w tle, gdy sama aplikacja nie znajduje się na pierwszym planie. Na przykład w większości przypadków informacje o lokalizacji nie powinny być dostępne w tle. Zamiast tego zaplanuj zadania za pomocą WorkManagera.

Powiązane
Usługa jest powiązana, gdy komponent aplikacji łączy się z nią, wywołując bindService(). Usługa powiązana oferuje interfejs klient-serwer, który umożliwia komponentom interakcję z usługą, wysyłanie żądań i otrzymywanie wyników, a nawet wykonywanie tego typu działań w ramach komunikacji międzyprocesowej (IPC). Usługa powiązana działa tylko wtedy, gdy jest z nią powiązany inny komponent aplikacji. Z usługą można powiązać jednocześnie wiele komponentów, ale gdy wszystkie zostaną usunięte, usługa jest zniszczona.

Chociaż w tej dokumentacji omawiane są zazwyczaj oddzielnie usługi utworzone i powiązane, usługa może działać w obie strony – można ją uruchomić (w sposób nieokreślony) oraz zezwolić na tworzenie powiązania. Wystarczy, że wdrożysz kilka metod wywołania zwrotnego: onStartCommand(), aby umożliwić komponentom uruchamianie go, i onBind(), aby umożliwić wiązanie.

Niezależnie od tego, czy usługa jest uruchomiona, powiązana czy też jednocześnie, dowolny komponent aplikacji może korzystać z niej (nawet z osobnej aplikacji) w taki sam sposób, w jaki każdy komponent może używać aktywności – rozpoczynając go od elementu Intent. W pliku manifestu możesz jednak zadeklarować usługę jako prywatną i zablokować dostęp do niej innym aplikacjom. Zostało to omówione dokładniej w sekcji dotyczącej deklarowania usługi w pliku manifestu.

Wybór między usługą a wątkiem

Usługa to po prostu komponent, który może działać w tle, nawet jeśli użytkownik nie wchodzi w interakcję z aplikacją. Dlatego usługę należy utworzyć tylko wtedy, gdy jest Ci potrzebna.

Jeśli musisz pracować poza swoim głównym wątkiem, ale tylko wtedy, gdy użytkownik wchodzi w interakcję z aplikacją, utwórz nowy wątek w kontekście innego komponentu aplikacji. Jeśli na przykład chcesz odtworzyć muzykę, ale tylko wtedy, gdy Twoja aktywność jest aktywna, możesz utworzyć wątek w usłudze onCreate(), uruchomić go w onStart(), a zatrzymać w onStop(). Rozważ też użycie pul wątków i wykonawców z pakietu java.util.concurrent lub współpracy Kotlin zamiast tradycyjnej klasy Thread. Więcej informacji o przenoszeniu wykonywania do wątków w tle znajdziesz w dokumencie Threading on Android (Wątki na Androidzie).

Pamiętaj, że nawet jeśli korzystasz z usługi, będzie ona nadal domyślnie działać w wątku głównym aplikacji. Pamiętaj więc, aby utworzyć nowy wątek w usłudze, jeśli wykonuje ona operacje intensywnie lub blokuje dostęp.

Podstawy

Aby utworzyć usługę, musisz utworzyć podklasę klasy Service lub użyć jednej z jej istniejących podklas. W swojej implementacji musisz zastąpić niektóre metody wywołania zwrotnego, które obsługują kluczowe aspekty cyklu życia usługi, i udostępnić mechanizm umożliwiający komponentom powiązanie z usługą (w stosownych przypadkach). Oto najważniejsze metody wywołania zwrotnego, które warto zastąpić:

onStartCommand()
System wywołuje tę metodę, wywołując metodę startService(), gdy inny komponent (np. aktywność) żąda uruchomienia usługi. Po uruchomieniu tej metody usługa zostaje uruchomiona i może działać w tle w nieskończoność. Jeśli wdrożysz tę usługę, Twoim obowiązkiem jest zatrzymanie usługi po zakończeniu jej działania przez wywołanie stopSelf() lub stopService(). Jeśli chcesz tylko podać wiązanie, nie musisz implementować tej metody.
onBind()
System wywołuje tę metodę, wywołując metodę bindService(), gdy inny komponent chce utworzyć powiązanie z usługą (np. aby wykonać RPC). W swojej implementacji tej metody musisz udostępnić interfejs używany klientom do komunikacji z usługą przez zwrócenie żądania IBinder. Musisz zawsze implementować tę metodę, ale jeśli jednak nie chcesz zezwalać na wiązanie, musisz zwracać wartość null.
onCreate()
System wywołuje tę metodę, aby wykonać jednorazową procedurę konfiguracji podczas pierwszego tworzenia usługi (przed wywołaniem onStartCommand() lub onBind()). Jeśli usługa jest już uruchomiona, metoda ta nie jest wywoływana.
onDestroy()
System wywołuje tę metodę, gdy usługa nie jest już używana i jest niszczona. Twoja usługa powinna go zaimplementować, aby wyczyścić wszelkie zasoby, takie jak wątki, zarejestrowane detektory i odbiorniki. Jest to ostatnie połączenie odebrane przez usługę.

Jeśli komponent uruchomi usługę, wywołując startService() (co skutkuje wywołaniem funkcji onStartCommand()), usługa będzie działać, dopóki nie zatrzyma się przy użyciu polecenia stopSelf() lub dopóki inny komponent go nie zatrzyma, wywołując stopService().

Jeśli komponent wywoła funkcję bindService() w celu utworzenia usługi, a onStartCommand() nie zostanie wywołany, usługa będzie działać tylko wtedy, gdy komponent jest z nią powiązany. Po usunięciu powiązania usługi od wszystkich klientów system ją niszczy.

System Android zatrzymuje usługę tylko wtedy, gdy brakuje pamięci, i musi przywracać zasoby systemowe przeznaczone do działania, które skupia się na użytkowniku. Jeśli usługa jest powiązana z aktywnością ukierunkowaną na użytkownika, prawdopodobieństwo jej zamknięcia jest mniejsze. Jeśli usługa jest zadeklarowana, że ma działać na pierwszym planie, rzadko jest zatrzymywana. Jeśli usługa jest uruchomiona przez długi czas, system z czasem obniża swoją pozycję na liście zadań w tle, przez co staje się ona wysoce podatna na śmierć. Jeśli usługa jest uruchomiona, musisz ją zaprojektować tak, aby płynnie obsługiwała ponowne uruchomienia przez system. Jeśli system zakończy działanie usługi, uruchomi ją ponownie, gdy tylko zasoby staną się dostępne. Zależy to też od wartości zwracanej z funkcji onStartCommand(). Więcej informacji o tym, kiedy system może zniszczyć usługę, znajdziesz w dokumencie Procesy i podział na wątki.

W kolejnych sekcjach dowiesz się, jak tworzyć metody usługi startService() i bindService(), a także jak używać ich z innych komponentów aplikacji.

Zadeklarowanie usługi w pliku manifestu

Wszystkie usługi musisz zadeklarować w pliku manifestu aplikacji, podobnie jak w przypadku aktywności i innych komponentów.

Aby zadeklarować usługę, dodaj element <service> jako element podrzędny elementu <application>. Oto przykład:

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

Więcej informacji o deklarowaniu usługi w pliku manifestu znajdziesz w dokumentacji elementu <service>.

W elemencie <service> możesz uwzględnić inne atrybuty, aby zdefiniować właściwości, takie jak uprawnienia wymagane do uruchomienia usługi i proces jej działania. Atrybut android:name jest jedynym wymaganym atrybutem – określa nazwę klasy usługi. Po opublikowaniu aplikacji nie zmieniaj tej nazwy, aby uniknąć ryzyka złamania kodu ze względu na zależność od wyraźnych intencji uruchomienia lub powiązania usługi (przeczytaj post na blogu Rzeczy, których nie można zmienić).

Uwaga: aby zapewnić bezpieczeństwo aplikacji, podczas uruchamiania Service zawsze używaj wyraźnej intencji i nie deklaruj filtrów intencji dla swoich usług. Korzystanie z niejawnej intencji uruchomienia usługi stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa odpowiada na intencję, a użytkownik nie może zobaczyć, która usługa się uruchamia. Począwszy od Androida 5.0 (poziom interfejsu API 21) system zgłasza wyjątek, jeśli wywołujesz funkcję bindService() z intencją niejawną.

Aby mieć pewność, że usługa jest dostępna tylko dla Twojej aplikacji, dodaj atrybut android:exported i ustaw go na false. Sprawia to, że inne aplikacje nie uruchamiają usługi, nawet jeśli używasz wyraźnej intencji.

Uwaga: użytkownicy mogą zobaczyć, jakie usługi działają na ich urządzeniach. Jeśli zobaczą usługę, której nie rozpoznają lub której nie zaufają, mogą ją zatrzymać. Aby uniknąć przypadkowego zatrzymania usługi przez użytkowników, musisz dodać atrybut android:description do elementu <service> w manifeście aplikacji. W opisie zamieść krótkie zdanie wyjaśniające, jak działa ta usługa i jakie są jej zalety.

Tworzę uruchomioną usługę

Uruchomiona usługa to usługa, której inny komponent rozpoczyna od wywołania startService(), co skutkuje wywołaniem metody onStartCommand() usługi.

Po uruchomieniu usługi ma ona cykl życia niezależny od komponentu, który ją uruchomił. Usługa może działać w tle przez czas nieokreślony, nawet jeśli komponent, który ją uruchomił, zostanie zniszczony. Usługa powinna się więc zatrzymać po zakończeniu zadania, wywołując stopSelf(). W przeciwnym razie inny komponent może ją zatrzymać, wywołując metodę stopService().

Komponent aplikacji, na przykład aktywność, może uruchomić usługę, wywołując usługę startService() i przekazując Intent, który określa usługę i zawiera wszelkie dane, które ma ona wykorzystywać. Usługa odbiera ten obiekt Intent w metodzie onStartCommand().

Załóżmy, że działanie musi zapisać pewne dane w bazie danych online. Aktywność może uruchomić usługę towarzyszącą i przekazać jej dane do zapisania, przekazując intencję do startService(). Usługa odbiera intencję w elemencie onStartCommand(), łączy się z internetem i wykonuje transakcję w bazie danych. Po zakończeniu transakcji usługa zatrzymuje się i zostaje zniszczona.

Uwaga: usługa działa w tym samym procesie co aplikacja, w której jest zadeklarowana, oraz domyślnie w jej głównym wątku. Jeśli Twoja usługa wykonuje intensywne lub blokujące operacje w czasie, gdy użytkownik wchodzi w interakcję z aktywnością tej samej aplikacji, spowalnia ona działanie. Aby uniknąć wpływu na wydajność aplikacji, uruchom nowy wątek w usłudze.

Klasa Service jest klasą podstawową wszystkich usług. Gdy rozszerzasz tę klasę, ważne jest, aby utworzyć nowy wątek, w którym usługa może ukończyć wszystkie swoje zadania. Usługa domyślnie używa głównego wątku aplikacji, co może spowolnić wykonywanie jej działań.

Platforma Androida udostępnia też podklasę IntentService klasy Service, która używa wątku roboczego do obsługi wszystkich żądań startowych, po kolei. W przypadku nowych aplikacji nie zalecamy korzystania z tej klasy, ponieważ nie będzie dobrze działać na urządzeniach z Androidem 8 Oreo z powodu limitów wykonywania w tle. Co więcej, ta funkcja jest wycofywana w Androidzie 11. Możesz użyć JobIntentService jako zamiennika IntentService, który jest zgodny z nowszymi wersjami Androida.

W sekcjach poniżej znajdziesz informacje o tym, jak możesz wdrożyć własną usługę niestandardową. Jednak w większości przypadków zdecydowanie zalecamy korzystanie z WorkManagera. Zapoznaj się ze wskazówkami dotyczącymi przetwarzania w tle na Androidzie, aby sprawdzić, czy znajdziesz rozwiązanie, które spełnia Twoje potrzeby.

Rozszerzanie klasy Service

Możesz rozszerzyć klasę Service na obsługę każdej intencji przychodzącej. Podstawowa implementacja może wyglądać tak:

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

Przykładowy kod obsługuje wszystkie połączenia przychodzące w interfejsie onStartCommand() i publikuje efekty pracy w usłudze Handler w wątku w tle. Działa ona tak samo jak IntentService i przetwarza wszystkie żądania kolejno, jeden po drugim. Możesz zmienić kod, aby uruchamiać zadania w puli wątków, na przykład jeśli chcesz uruchomić wiele żądań jednocześnie.

Zwróć uwagę, że metoda onStartCommand() musi zwracać liczbę całkowitą. Liczba całkowita określa, jak system powinien kontynuować działanie usługi, gdy zostanie ona wyłączona przez system. Zwracana wartość z onStartCommand() musi być jedną z tych stałych:

START_NOT_STICKY
Jeśli system zamknie usługę po zwróceniu usługi onStartCommand(), nie twórz jej ponownie, chyba że istnieją oczekujące zamiary na dostarczenie. Jest to najbezpieczniejszy sposób uniknięcia uruchamiania usługi, gdy nie jest to konieczne, a aplikacja może po prostu ponownie uruchomić dowolne nieukończone zadania.
START_STICKY
Jeśli system wyłączy usługę po zwróceniu usługi onStartCommand(), odtwórz ją i wywołaj metodę onStartCommand(), ale nie ponownie przesyłaj ostatniej intencji. Zamiast tego system wywołuje intencję onStartCommand() z intencją null, chyba że istnieją oczekujące intencje uruchomienia usługi. Wtedy intencje te są realizowane. Jest to przydatne w przypadku odtwarzaczy multimedialnych (lub podobnych usług), które nie wykonują poleceń, ale działają w nieskończoność i czekają na wykonanie zadania.
START_REDELIVER_INTENT
Jeśli system zamknie usługę po zwróceniu usługi onStartCommand(), odtwórz ją i wywołaj polecenie onStartCommand() z ostatnią intencją, która została dostarczona do usługi. Wszystkie oczekujące intencje są dostarczane w kolejności. Jest to przydatne w przypadku usług, które aktywnie wykonują zadania, które należy wznowić natychmiast, takich jak pobieranie pliku.

Więcej informacji o tych zwracanych wartościach znajdziesz w dokumentacji referencyjnej poszczególnych stałych.

Uruchamianie usługi

Możesz uruchomić usługę z aktywności lub innego komponentu aplikacji, przekazując Intent do startService() lub startForegroundService(). System Android wywołuje metodę onStartCommand() usługi i przekazuje do niej Intent, która określa, którą usługę uruchomić.

Uwaga: jeśli aplikacja jest kierowana na interfejs API na poziomie 26 lub wyższym, system nakłada ograniczenia na używanie i tworzenie usług w tle, chyba że sama aplikacja działa na pierwszym planie. Jeśli aplikacja musi utworzyć usługę na pierwszym planie, powinna wywołać startForegroundService(). Ta metoda tworzy usługę w tle, ale sygnalizuje systemowi, że usługa ta awansuje na pierwszy plan. Po utworzeniu usługi musi ona wywołać swoją metodę startForeground() w ciągu 5 sekund.

Na przykład działanie może uruchomić przykładową usługę z poprzedniej sekcji (HelloService) przy użyciu wyraźnej intencji z elementem startService(), jak w tym przykładzie:

Kotlin

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

Java

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

Metoda startService() jest zwracana natychmiast, a system Android wywołuje metodę onStartCommand() usługi. Jeśli usługa nie jest jeszcze uruchomiona, system najpierw wywoła metodę onCreate(), a następnie onStartCommand().

Jeśli usługa nie udostępnia też powiązania, intencja dostarczana za pomocą startService() jest jedynym trybem komunikacji między komponentem aplikacji a usługą. Jeśli jednak chcesz, aby usługa odesłała wynik, klient, który uruchamia usługę, może utworzyć PendingIntent dla transmisji (z użyciem getBroadcast()) i przekazać go do usługi w Intent, która uruchamia usługę. Usługa może następnie wykorzystać transmisję, aby przedstawić wynik.

Wielokrotne żądania uruchomienia usługi powodują kilka odpowiadających jej wywołań onStartCommand() usługi. Do zatrzymania usługi wymagane jest jednak tylko jedno żądanie jej zatrzymania (za pomocą stopSelf() lub stopService()).

Zatrzymywanie usługi

Uruchomiona usługa musi zarządzać własnym cyklem życia. Oznacza to, że system nie zatrzymuje ani nie niszczy usługi, chyba że musi przywrócić pamięć systemową, a usługa będzie nadal działać po zwróceniu funkcji onStartCommand(). Usługa musi się zatrzymać samodzielnie, wywołując stopSelf(), albo inny komponent może ją zatrzymać, wywołując metodę stopService().

Po zażądaniu zatrzymania za pomocą stopSelf() lub stopService() system niszczy usługę najszybciej, jak to możliwe.

Jeśli Twoja usługa jednocześnie obsługuje wiele żądań wysyłanych do onStartCommand(), nie zatrzymuj jej po zakończeniu przetwarzania żądania startowego, ponieważ mogło zostać wysłane nowe żądanie startowe (zatrzymanie na końcu pierwszego żądania spowoduje zakończenie drugiego). Aby uniknąć tego problemu, możesz użyć polecenia stopSelf(int), aby żądanie zatrzymania usługi zawsze było oparte na ostatnim żądaniu uruchomienia. Oznacza to, że gdy wywołujesz stopSelf(int), przekazujesz identyfikator żądania rozpoczęcia (startId dostarczonego do onStartCommand()), któremu odpowiada Twoje żądanie zatrzymania. Następnie, jeśli usługa otrzyma nowe żądanie uruchomienia, zanim będzie można wywołać stopSelf(int), identyfikator nie będzie się zgadzać i usługa nie zatrzyma się.

Uwaga: aby uniknąć marnowania zasobów systemu i zużywać energii z baterii, zadbaj o to, by aplikacja zatrzymywała swoje usługi po zakończeniu pracy. W razie potrzeby inne komponenty mogą zatrzymać usługę, wywołując stopService(). Nawet jeśli włączysz powiązanie dla usługi, musisz zawsze samodzielnie zatrzymać usługę, jeśli kiedykolwiek otrzyma ona wywołanie do onStartCommand().

Więcej informacji o cyklu życia usługi znajdziesz w sekcji dotyczącej zarządzania cyklem życia usługi poniżej.

Tworzenie powiązanej usługi

Usługa powiązana to usługa, która umożliwia komponentom aplikacji powiązanie z nią przez wywołanie bindService() w celu utworzenia długotrwałego połączenia. Zwykle nie zezwala to komponentom na uruchamianie go przez wywołanie startService().

Utwórz powiązaną usługę, jeśli chcesz wejść w interakcję z tą usługą z poziomu działań i innych komponentów aplikacji lub aby udostępnić niektóre funkcje aplikacji innym aplikacjom za pomocą komunikacji międzyprocesowej (IPC).

Aby utworzyć usługę powiązaną, zaimplementuj metodę wywołania zwrotnego onBind(), która zwraca IBinder, który określa interfejs komunikacji z usługą. Inne komponenty aplikacji mogą wtedy wywołać bindService(), aby pobrać interfejs i rozpocząć wywoływanie metod w usłudze. Usługa działa tylko po to, aby obsługiwać powiązany z nią komponent aplikacji. Jeśli więc nie ma z nią żadnych komponentów powiązanych, system niszczy ją. Nie musisz zatrzymywać powiązanej usługi w taki sam sposób, jak przy uruchamianiu usługi za pomocą onStartCommand().

Aby utworzyć powiązaną usługę, musisz zdefiniować interfejs określający sposób, w jaki klient może komunikować się z usługą. Interfejs między usługą a klientem musi być implementacją typu IBinder i tym, co Twoja usługa musi zwrócić z metody wywołania zwrotnego onBind(). Gdy klient otrzyma IBinder, może zacząć korzystać z usługi przez ten interfejs.

Z usługą może powiązać jednocześnie wielu klientów. Gdy klient zakończy interakcję z usługą, wywołuje metodę unbindService(), aby usunąć powiązanie. Gdy z usługą nie są powiązane żadne klienty, system niszczy usługę.

Powiązaną usługę można wdrożyć na wiele sposobów, a ich implementacja jest bardziej złożona niż w przypadku rozpoczętej usługi. Z tego względu omówienie powiązanych usług znajduje się w osobnym dokumencie na temat usług granicznych.

Wysyłanie powiadomień do użytkownika

Gdy usługa jest uruchomiona, może powiadamiać użytkownika o zdarzeniach przy użyciu powiadomień na pasku powiadomień lub powiadomień na pasku stanu.

Powiadomienie na pasku powiadomień to wiadomość, która pojawia się u góry bieżącego okna, zanim zniknie. Powiadomienie na pasku stanu ma na pasku stanu ikonę z komunikatem, którą użytkownik może wybrać, by wykonać działanie (np. rozpocząć aktywność).

Zazwyczaj powiadomienia na pasku stanu są najlepszą metodą, gdy działania w tle, takie jak zakończenie pobierania pliku, zostały zakończone i użytkownik może teraz wykonać odpowiednie czynności. Gdy użytkownik wybierze powiadomienie w widoku rozwiniętym, może ono rozpocząć działanie (np. wyświetlić pobrany plik).

Zarządzanie cyklem życia usługi

Cykl życia usługi jest znacznie prostszy niż cykl działania. Jeszcze ważniejsze jest jednak, aby dokładnie przeanalizować sposób tworzenia i niszczenia usługi, ponieważ usługa może działać w tle bez wiedzy użytkownika.

Cykl życia usługi – od momentu jej utworzenia do zniszczenia – może podążać jedną z tych 2 ścieżek:

  • Uruchomiona usługa

    Usługa jest tworzona, gdy inny komponent wywołuje startService(). Usługa będzie działać w nieskończoność i musi się zatrzymać, wywołując stopSelf(). Usługę może też zatrzymać inny komponent, wywołując stopService(). Gdy usługa zostanie zatrzymana, system ją niszczy.

  • Usługa powiązana

    Usługa jest tworzona, gdy inny komponent (klient) wywołuje metodę bindService(). Klient komunikuje się z usługą przez interfejs IBinder. Klient może zakończyć połączenie, wywołując unbindService(). Z tą samą usługą może powiązać się wielu klientów, a gdy wszystkie je zostaną usunięte, system niszczy usługę. Usługa nie musi się zatrzymywać.

Te 2 ścieżki nie są całkowicie oddzielne. Możesz utworzyć powiązanie z usługą, która została już uruchomiona z startService(). Możesz na przykład uruchomić usługę muzyki w tle, wywołując funkcję startService() z podanym adresem Intent identyfikującym muzykę do odtworzenia. Później, na przykład, gdy użytkownik zechce zachować kontrolę nad odtwarzaczem lub uzyskać informacje o bieżącym utworze, aktywność może powiązać z usługą, wywołując bindService(). W takich przypadkach stopService() lub stopSelf() nie zatrzymuje usługi, dopóki wszyscy klienci nie rozłączą się.

Wdrażanie wywołań zwrotnych cyklu życia

Podobnie jak aktywność, usługa ma metody wywołania zwrotnego cyklu życia, które możesz wdrożyć, aby monitorować zmiany stanu i wykonywać zadania w odpowiednich momentach. Ta szkieletowa usługa prezentuje poszczególne metody cyklu życia:

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
    }
}

Uwaga: w przeciwieństwie do metod wywołań zwrotnych cyklu życia aktywności nie musisz wywoływać implementacji tych metod dotyczących klasy nadrzędnej.

Rysunek 2. Cykl życia usługi. Diagram po lewej stronie przedstawia cykl życia, gdy usługa jest tworzona za pomocą startService(), a diagram po prawej stronie pokazuje cykl życia, gdy usługa została utworzona za pomocą bindService().

Rysunek 2 przedstawia typowe metody wywołania zwrotnego dla usługi. Chociaż ten wykres oddziela usługi utworzone przez startService() od tych utworzonych przez bindService(), pamiętaj, że każda usługa, niezależnie od tego, jak została uruchomiona, może umożliwić klientom powiązanie z nią. Usługa, której początkiem jest onStartCommand() (przez klienta wywołującego startService()), może nadal otrzymywać połączenia z numerem onBind() (gdy klient wywołuje metodę bindService()).

Dzięki wdrożeniu tych metod możesz monitorować te 2 zagnieżdżone pętle cyklu życia usługi:

  • Cały okres użytkowania usługi ma miejsce między wywołaniem metody onCreate() a momentem zwrócenia obiektu onDestroy(). Podobnie jak aktywność, usługa przeprowadza początkową konfigurację w onCreate() i zwalnia wszystkie pozostałe zasoby w zadaniu onDestroy(). Na przykład usługa odtwarzania muzyki może utworzyć wątek, w którym muzyka jest odtwarzana w usłudze onCreate(), a następnie zatrzymać wątek w elemencie onDestroy().

    Uwaga: metody onCreate() i onDestroy() są wywoływane w przypadku wszystkich usług, niezależnie od tego, czy zostały one utworzone przez usługę startService() czy bindService().

  • Okres aktywności usługi zaczyna się od wywołania funkcji onStartCommand() lub onBind(). Każda metoda otrzymuje Intent, który został przekazany do startService() lub bindService().

    Jeśli usługa zostanie uruchomiona, aktywny czas trwania zakończy się w tym samym momencie, kiedy zakończy się cały okres użytkowania (usługa jest nadal aktywna po zwróceniu usługi onStartCommand()). Jeśli usługa jest powiązana, aktywny czas trwania kończy się, gdy zwracany jest element onUnbind().

Uwaga: mimo że uruchomiona usługa jest zatrzymywana przez wywołanie stopSelf() lub stopService(), nie ma dla niej odpowiedniego wywołania zwrotnego (nie występuje wywołanie zwrotne onStop()). Jeśli usługa nie jest powiązana z klientem, system niszczy ją po zatrzymaniu usługi – jedynym odbieranym wywołaniem zwrotnym jest onDestroy().

Więcej informacji o tworzeniu usługi, która umożliwia powiązanie usługi, znajdziesz w dokumencie Bound Services (Usługi graniczne), który zawiera więcej informacji o metodzie wywołania zwrotnego onRebind() w sekcji dotyczącej zarządzania cyklem życia powiązanej usługi.