Przegląd usług

Service to komponent aplikacji, który może wykonywać długotrwałe operacje w tle. Nie ma interfejsu. Po uruchomieniu usługa może działać przez pewien czas, nawet gdy użytkownik przełączy się na inną aplikację. Komponent może też się łączyć z usługą, aby z nią współpracować, a nawet przeprowadzać komunikację międzyprocesową (IPC). Usługa może na przykład obsługiwać transakcje sieciowe, odtwarzać muzykę, wykonywać operacje wejścia/wyjścia z pliku lub wchodzić w interakcje z dostawcą treści, a wszystko to w tle.

Uwaga: usługa działa w głównym wątku procesu, który ją obsługuje. O ile nie określisz inaczej, usługa nie tworzy własnego wątku i nie działa w osobnym procesie. Aby uniknąć błędów typu Aplikacja nie odpowiada (ANR), operacje blokujące należy wykonywać w oddzielnym wątku w usłudze.

Rodzaje usług

Oto 3 rodzaje usług:

Pierwszy plan

Usługa na pierwszym planie wykonuje działanie, które jest zauważalne dla użytkownika. Na przykład aplikacja do odtwarzania dźwięku używa usługi na pierwszym planie do odtwarzania ścieżki audio. Usługi na pierwszym planie muszą wyświetlać powiadomienie. Usługi działające na pierwszym planie działają nadal, nawet gdy użytkownik nie wchodzi w interakcję z aplikacją.

Jeśli korzystasz z usługi na pierwszym planie, musisz wyświetlić powiadomienie, aby użytkownicy byli świadomi, że usługa jest uruchomiona. Nie można zamknąć tego powiadomienia, 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 umożliwia elastyczne planowanie zadań i w razie potrzeby uruchamianie tych zadań jako usług na pierwszym planie. W wielu przypadkach korzystanie z WorkManagera jest lepsze niż bezpośrednie korzystanie z usług na pierwszym planie.

Tło
Usługa działająca w tle wykonuje operację, której użytkownik nie zauważa bezpośrednio. Jeśli na przykład aplikacja używa usługi do kompresji pamięci, zwykle jest to usługa działająca w tle.

Uwaga: jeśli Twoja aplikacja jest kierowana na interfejs API na poziomie 26 lub wyższym, system nakłada ograniczenia na usługi działające w tle, gdy aplikacja nie jest na pierwszym planie. W większości przypadków nie należy dostępować do informacji o lokalizacji w tle. Zamiast tego planuj zadania za pomocą WorkManagera.

Powiązane
Usługa jest powiązana, gdy komponent aplikacji łączy się z nią przez wywołanie bindService(). Usługa związana udostępnia interfejs klient-serwer, który umożliwia komponentom interakcję z usługą, wysyłanie żądań i odbieranie wyników, a nawet wykonywanie tych czynności w ramach procesów za pomocą komunikacji międzyprocesowej (IPC). Usługa powiązana działa tylko wtedy, gdy jest powiązana z innym komponentem aplikacji. Do usługi może być jednocześnie przywiązanych wiele komponentów, ale gdy wszystkie z nich zostaną odłączone, usługa zostanie zniszczona.

Chociaż w tej dokumentacji usługi uruchamiane i wiązane są omawiane osobno, Twoja usługa może działać w obu kierunkach – może być uruchamiana (aby działać w nieskończoność) i umożliwiać wiązanie. Wystarczy, że zaimplementujesz kilka metod wywołania: onStartCommand(), aby umożliwić uruchamianie go przez komponenty, oraz onBind(), aby umożliwić wiązanie.

Niezależnie od tego, czy usługa jest uruchomiona, powiązana czy też w obu tych stanach, każdy komponent aplikacji może korzystać z usługi (nawet z osobnej aplikacji) w taki sam sposób, w jaki każdy komponent może korzystać z działalności – przez uruchomienie jej za pomocą instrukcji Intent. Możesz jednak zadeklarować usługę jako prywatną w pliku manifestu i zablokować dostęp z innych aplikacji. Więcej informacji na ten temat znajdziesz w sekcji Deklarowanie usługi w pliku manifestu.

Wybieranie między usługą a wątkiem

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

Jeśli musisz wykonywać zadania poza wątkiem głównym, 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 odtwarzać muzykę tylko podczas wykonywania aktywności, możesz utworzyć wątek w onCreate(), uruchomić go w onStart() i zatrzymać w onStop(). Zamiast tradycyjnej klasy Thread rozważ użycie puli wątków i wykonawców z pakietu java.util.concurrent lub korobon w Kotlinie. Więcej informacji o przenoszeniu wykonania na wątki w tle znajdziesz w dokumentacji Programowanie wątków na Androidzie.

Pamiętaj, że jeśli używasz usługi, to domyślnie działa ona w głównym wątku aplikacji, więc jeśli wykonuje operacje intensywne lub blokujące, musisz utworzyć nowy wątek w ramach usługi.

Podstawy

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

onStartCommand()
System wywołuje tę metodę, wywołując startService(), gdy inny komponent (np. aktywność) poprosi o uruchomienie usługi. Gdy ta metoda zostanie wykonana, usługa zostanie uruchomiona i może działać w tle przez nieograniczony czas. Jeśli zdecydujesz się na to rozwiązanie, po zakończeniu pracy usługi będziesz musiał(-a) ją wyłączyć, dzwoniąc pod numer stopSelf() lub stopService(). Jeśli chcesz tylko zapewnić działanie na żądanie, nie musisz stosować tej metody.
onBind()
System wywołuje tę metodę, wywołując bindService(), gdy inny komponent chce się połączyć z usługą (np. w celu wykonania wywołania RPC). Podczas implementacji tej metody musisz udostępnić interfejs, którego klienci używają do komunikacji z usługą, zwracając IBinder. Musisz zawsze zaimplementować tę metodę. Jeśli jednak nie chcesz zezwalać na wiązanie, zwracaj null.
onCreate()
System wywołuje tę metodę, aby wykonać jednorazowe procedury konfiguracyjne podczas początkowego tworzenia usługi (przed wywołaniem metod onStartCommand() lub onBind()). Jeśli usługa jest już uruchomiona, ta metoda 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 zaimplementować tę metodę, aby zwolnić zasoby, takie jak wątki, zarejestrowane odbiorniki lub odbiorniki. Jest to ostatnie wywołanie, które usługa otrzymuje.

Jeśli komponent uruchamia usługę przez wywołanie metody startService() (co powoduje wywołanie metody onStartCommand()), usługa działa do momentu, gdy zatrzyma ją metoda stopSelf() lub inny komponent wywoła metodę stopService().

Jeśli komponent wywołuje funkcję bindService(), aby utworzyć usługę, a funkcja onStartCommand() nie nie jest wywoływana, usługa działa tylko wtedy, gdy komponent jest z nią powiązany. Gdy usługa zostanie odłączona od wszystkich klientów, system ją usuwa.

System Android zatrzymuje usługę tylko wtedy, gdy brakuje pamięci i musi odzyskać zasoby systemowe dla aktywności, na której skupia się użytkownik. Jeśli usługa jest powiązana z działaniem, które ma na celu skupienie uwagi użytkownika, jest mniej podatna na zabicie. Jeśli usługa jest deklarowana jako działająca na pierwszym planie, jest rzadko zabijana. Jeśli usługa jest uruchomiona i długotrwała, system z czasem obniża jej pozycję na liście zadań w tle, przez co staje się ona bardzo podatna na zabicie. Jeśli usługa jest uruchomiona, musisz ją zaprojektować tak, aby sprawnie obsługiwała restarty przez system. Jeśli system zabije usługę, ponownie ją uruchomi, gdy tylko zasoby staną się dostępne, ale zależy to też od wartości zwracanej przez onStartCommand(). Więcej informacji o tym, kiedy system może zniszczyć usługę, znajdziesz w dokumencie Procesy i wątki.

W następnych sekcjach dowiesz się, jak tworzyć metody usługi startService()bindService() oraz jak ich używać z innych komponentów aplikacji.

Deklarowanie usługi w pliku manifestu

Wszystkie usługi musisz zadeklarować w pliku manifestu aplikacji, tak samo 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 elemencie <service>.

W elemencie <service> możesz podać też inne atrybuty, aby zdefiniować właściwości, takie jak uprawnienia wymagane do uruchomienia usługi i proces, w którym ma ona działać. Atrybut android:name jest jedynym wymaganym atrybutem – określa on nazwę klasy usługi. Po opublikowaniu aplikacji nie zmieniaj tej nazwy, aby uniknąć ryzyka uszkodzenia kodu spowodowanego zależnością od jawnych intencji służących do uruchamiania lub wiązania usługi (przeczytaj wpis na blogu Czego nie można zmieniać).

Uwaga: aby zapewnić bezpieczeństwo aplikacji, podczas uruchamiania Service zawsze używaj jawnej intencji i nie deklaruj filtrów intencji dla swoich usług. Używanie do uruchamiania usługi intencji ogólnej stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa odpowiada na intencję, a użytkownik nie widzi, która usługa się uruchamia. Począwszy od Androida 5.0 (poziom interfejsu API 21) system zgłasza wyjątek, jeśli wywołasz funkcję bindService() z ukrytą intencją.

Aby zapewnić, że usługa będzie dostępna tylko dla Twojej aplikacji, dodaj atrybut android:exported i ustaw go na false. Dzięki temu inne aplikacje nie będą mogły uruchamiać Twojej usługi, nawet jeśli używają do tego wyraźnej intencji.

Uwaga: użytkownicy mogą sprawdzać, jakie usługi są uruchomione na ich urządzeniu. Jeśli widzą usługę, której nie rozpoznają lub której nie ufają, mogą ją wyłączyć. Aby uniknąć przypadkowego zatrzymania usługi przez użytkowników, musisz dodać atrybut android:description do elementu <service> w pliku manifestu aplikacji. W opisie krótko wyjaśnij, na czym polega usługa i jakie korzyści przynosi.

Tworzenie uruchomionej usługi

Uruchomiona usługa to usługa uruchomiona przez inny komponent przez wywołanie funkcji startService(), co powoduje wywołanie metody onStartCommand() usługi.

Gdy usługa zostanie uruchomiona, ma cykl życia niezależny od komponentu, który ją uruchomił. Usługa może działać w tle w nieskończoność, nawet jeśli komponent, który ją uruchomił, został zniszczony. Dlatego usługa powinna się zatrzymać, gdy wykona swoje zadanie, wywołując funkcję stopSelf(), lub inny komponent może ją zatrzymać, wywołując funkcję stopService().

Komponent aplikacji, np. aktywność, może uruchomić usługę, wywołując funkcję startService() i przekazując obiekt Intent, który określa usługę i zawiera wszystkie dane, których usługa ma używać. Usługa otrzymuje tę wartość Intent w ramach metody onStartCommand().

Załóżmy na przykład, że dana aktywność wymaga zapisania niektórych danych 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 onStartCommand(), łączy się z internetem i wykonuje transakcję bazy 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, i domyślnie w głównym wątku tej aplikacji. Jeśli usługa wykonuje operacje intensywne lub blokujące, gdy użytkownik wchodzi w interakcję z działalnością w tej samej aplikacji, usługa spowalnia działanie tej aktywności. Aby uniknąć wpływu na wydajność aplikacji, utwórz nowy wątek w usłudze.

Klasa Service jest klasą podstawową dla wszystkich usług. Gdy rozszerzasz tę klasę, ważne jest, aby utworzyć nowy wątek, w którym usługa może wykonać całą swoją pracę. Usługa domyślnie używa głównego wątku aplikacji, co może spowolnić działanie każdej aktywności wykonywanej przez aplikację.

Framework Androida udostępnia też podklasę IntentService klasy Service, która używa wątku roboczego do obsługi wszystkich żądań startowych pojedynczo. Nie zalecamy używania tej klasy w przypadku nowych aplikacji, ponieważ nie będzie ona działać prawidłowo w Androidzie 8 Oreo i nowszych ze względu na wprowadzenie ograniczeń dotyczących wykonywania w tle. Ponadto od wersji 11 Androida jest ona wycofana. Możesz użyć JobIntentService jako zamiennika dla IntentService, który jest zgodny z nowszymi wersjami Androida.

W następnych sekcjach opisaliśmy, jak zaimplementować własną usługę niestandardową, ale w większości przypadków zalecamy używanie WorkManagera. Aby sprawdzić, czy jest dostępne rozwiązanie, które odpowiada Twoim potrzebom, zapoznaj się z przetwarzaniem w tle na Androidzie.

Rozszerzanie klasy Service

Aby obsłużyć każdą przychodzącą intencję, możesz rozszerzyć klasę Service. Oto jak może wyglądać podstawowa implementacja:

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 przychodzące wywołania w funkcji onStartCommand() i przesyła dane do funkcji Handler działającej w wątku tlenowym. Działa on tak samo jak IntentService i przetwarza wszystkie żądania po kolei, jedno po drugim. Jeśli chcesz na przykład uruchomić kilka żądań jednocześnie, możesz zmienić kod, aby wykonywał on zadania w poolu wątków.

Zwróć uwagę, że metoda onStartCommand() musi zwracać liczbę całkowitą. Liczba całkowita to wartość określająca, jak system powinien kontynuować usługę w przypadku jej zakończenia przez system. Wartość zwracana przez funkcję onStartCommand() musi być jedną z tych stałych:

START_NOT_STICKY
Jeśli system zatrzymuje usługę po onStartCommand(), nie twórz jej ponownie, chyba że są oczekujące intencje do dostarczenia. Jest to najbezpieczniejsza opcja, która pozwala uniknąć uruchamiania usługi, gdy nie jest to konieczne, a aplikacja może po prostu wznowić nieukończone zadania.
START_STICKY
Jeśli system zakończy działanie usługi po onStartCommand(), ponownie utwórz usługę i wywołaj funkcję onStartCommand(), ale nie ponownie wysyłaj ostatniej intencji. Zamiast tego system wywołuje onStartCommand() z zapytaniem o null, chyba że są oczekujące intencje uruchomienia usługi. W takim przypadku te intencje są realizowane. Ta opcja jest odpowiednia dla odtwarzaczy multimediów (lub podobnych usług), które nie wykonują poleceń, ale działają w nieskończoność i czekają na zadanie.
START_REDELIVER_INTENT
Jeśli system zakończy działanie usługi po zwróceniu onStartCommand(), utwórz ponownie usługę i wywołaj onStartCommand() z ostatnią intencją, która została dostarczona do usługi. Wszystkie oczekujące intencje są dostarczane po kolei. Ta opcja jest odpowiednia dla usług, które aktywnie wykonują zadanie, które powinno zostać natychmiast wznowione, np. pobieranie pliku.

Więcej informacji o tych wartościach zwracanych znajdziesz w powiązanej dokumentacji każdej stałej.

Uruchamianie usługi

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

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

Na przykład aktywność może uruchomić przykładową usługę z poprzedniej sekcji (HelloService) za pomocą jawnej intencji z startService(), jak pokazano tutaj:

Kotlin

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

Java

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

Metoda startService() zwraca natychmiast, a system Android wywołuje metodę onStartCommand() usługi. Jeśli usługa nie jest uruchomiona, system najpierw wywołuje funkcję onCreate(), a potem funkcję onStartCommand().

Jeśli usługa nie zapewnia też informacji o obowiązku, intencje przekazywane za pomocą startService() są jedynym sposobem komunikacji między komponentem aplikacji a usługą. Jeśli jednak chcesz, aby usługa wysłała wynik z powrotem, klient, który uruchamia usługę, może utworzyć PendingIntent dla transmisji (z getBroadcast()) i przekazać go usłudze w ramach Intent, która uruchamia usługę. Usługa może następnie użyć transmisji, aby uzyskać wynik.

Wiele żądań uruchomienia usługi powoduje wiele odpowiednich wywołań onStartCommand() usługi. Wystarczy jednak jedno żądanie przerwania usługi (z użyciem stopSelf() lub stopService()).

Zatrzymywanie usługi

Uruchomiona usługa musi zarządzać własnym cyklem życia. Oznacza to, że system nie zatrzyma ani nie zniszczy usługi, chyba że musi odzyskać pamięć systemową, a usługa nadal działa po powrocie onStartCommand(). Usługa musi się zatrzymać, wywołując funkcję stopSelf(), lub inny komponent może ją zatrzymać, wywołując funkcję stopService().

Gdy usługa zostanie zatrzymana za pomocą stopSelf() lub stopService(), system zniszczy ją tak szybko, jak to możliwe.

Jeśli usługa obsługuje wiele żądań do onStartCommand() jednocześnie, nie należy jej zatrzymywać po zakończeniu przetwarzania żądania uruchomienia, ponieważ może ona otrzymać nowe żądanie uruchomienia (zatrzymanie po pierwszym żądaniu spowoduje przerwanie drugiego). Aby uniknąć tego problemu, możesz użyć funkcji stopSelf(int), aby mieć pewność, że Twoja prośba o zatrzymanie usługi będzie zawsze oparta na najnowszej prośbie o uruchomienie. Oznacza to, że gdy wywołasz stopSelf(int), przekazujesz identyfikator żądania start (startId) wysłanego do onStartCommand(), do którego odnosi się żądanie stop. Jeśli usługa otrzyma nowe żądanie uruchomienia, zanim będzie można wywołać funkcję stopSelf(int), identyfikatory nie będą się zgadzać, a usługa nie przestanie działać.

Uwaga: aby nie marnować zasobów systemowych i nie zużywać energii baterii, upewnij się, że aplikacja przestaje świadczyć usługi, gdy skończy pracę. W razie potrzeby inne komponenty mogą zatrzymać usługę, wywołując funkcję stopService(). Nawet jeśli włączysz usługę wiązania, musisz ją zawsze samodzielnie zatrzymać, jeśli otrzyma ona połączenie z numeru onStartCommand().

Więcej informacji o cyklu życia usługi znajdziesz w sekcji Zarządzanie cyklem życia usługi.

Tworzenie usługi powiązanej

Powiązana usługa to usługa, która umożliwia komponentom aplikacji łączenie się z nią przez wywołanie funkcji bindService() w celu utworzenia trwałego połączenia. Ogólnie nie pozwala komponentom uruchomić go przez wywołanie metody startService().

Utwórz usługę powiązaną, gdy chcesz wchodzić w interakcje z usługą z aktywności i innych komponentów w aplikacji lub udostępniać niektóre funkcje aplikacji innym aplikacjom za pomocą komunikacji międzyprocesowej (IPC).

Aby utworzyć powiązaną usługę, zaimplementuj metodę wywołania zwrotnego onBind(), aby zwrócić obiekt IBinder, który definiuje interfejs do komunikacji z usługą. Inne komponenty aplikacji mogą następnie wywoływać funkcję 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, więc gdy nie ma żadnych komponentów powiązanych z usługą, system ją niszczy. Nie musisz zatrzymywać powiązanej usługi w taki sam sposób, jak w przypadku usługi uruchomionej za pomocą onStartCommand().

Aby utworzyć powiązaną usługę, musisz zdefiniować interfejs określający, jak klient może komunikować się z usługą. Ten interfejs między usługą a klientem musi być implementacją interfejsu IBinder i musi być zwracany przez usługę w ramach metody wywołania zwrotnego onBind(). Gdy klient otrzyma IBinder, może zacząć korzystać z usługi za pomocą tego interfejsu.

Do usługi może być jednocześnie przyłączonych wielu klientów. Gdy klient skończy korzystać z usługi, wywoła funkcję unbindService(), aby odłączyć się od niej. Jeśli nie ma klientów powiązanych z usługą, system ją niszczy.

Istnieje kilka sposobów implementacji usługi powiązanej, a jej wdrożenie jest bardziej skomplikowane niż w przypadku usługi uruchamianej. Z tych powodów informacje o usługach powiązanych znajdują się w oddzielnym dokumencie dotyczącym usług powiązanych.

Wysyłanie powiadomień do użytkownika

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

Powiadomienie w pasku powiadomień to wiadomość, która pojawia się na powierzchni bieżącego okna tylko przez chwilę, po czym znika. Powiadomienie na pasku stanu zawiera ikonę z wiadomością, którą użytkownik może wybrać, aby wykonać działanie (np. rozpocząć aktywność).

Powiadomienie w pasku stanu jest zwykle najlepszym rozwiązaniem, gdy praca w tle, np. pobieranie pliku, została zakończona i użytkownik może teraz podjąć odpowiednie działania. Gdy użytkownik wybierze powiadomienie w rozwiniętym widoku, może ono uruchomić działanie (np. wyświetlić pobrany plik).

Zarządzanie cyklem życia usługi

Cykl życia usługi jest znacznie prostszy niż cykl aktywności. Jeszcze ważniejsze jest jednak zwrócenie uwagi na sposób tworzenia i usuwania usługi, ponieważ może ona działać w tle bez wiedzy użytkownika.

Cykl życia usługi (od jej utworzenia do jej usunięcia) może przebiegać w jednym z tych 2 kierunków:

  • Uruchomiona usługa

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

  • powiązana usługa

    Usługa jest tworzona, gdy inny komponent (klient) wywołuje funkcję bindService(). Następnie klient komunikuje się z usługą za pomocą interfejsu IBinder. Klient może zamknąć połączenie, wywołując funkcję unbindService(). Do tej samej usługi może być powiązanych wielu klientów, a gdy wszyscy się odłączą, system ją usuwa. Usługa nie musi się sama zatrzymać.

Te 2 ścieżki nie są całkowicie oddzielne. Możesz połączyć się z usługą, która została już uruchomiona za pomocą startService(). Możesz na przykład uruchomić usługę odtwarzania muzyki w tle, wywołując startService() z Intent, który identyfikuje muzykę do odtworzenia. Później, gdy użytkownik chce przejąć kontrolę nad odtwarzaczem lub uzyskać informacje o bieżącym utworze, aktywność może zostać powiązana z usługą przez wywołanie funkcji bindService(). W takich przypadkach pole stopService() lub stopSelf() nie zatrzymuje usługi, dopóki wszyscy klienci nie zostaną odwiązani.

Implementowanie wywołań zwrotnych cyklu życia

Podobnie jak w przypadku aktywności, usługa ma metody wywołania w ramach cyklu życia, które można zaimplementować, aby monitorować zmiany stanu usługi i wykonywać zadania we właściwym czasie. Poniższy szkielet usługi demonstruje każdą z metod 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ływania w ramach cyklu życia aktywności nie musisz wywoływać implementacji tych metod w superklasie.

Rysunek 2. Cykl życia usługi. Diagram po lewej stronie przedstawia cykl życia usługi utworzonej za pomocą funkcji startService(), a diagram po prawej stronie – cykl życia usługi utworzonej za pomocą funkcji bindService().

Rysunek 2 przedstawia typowe metody wywołania zwrotnego dla usługi. Chociaż na rysunku usługi utworzone przez startService() są odseparowane od tych utworzonych przez bindService(), pamiętaj, że każda usługa, niezależnie od tego, jak została uruchomiona, może potencjalnie zezwolić klientom na jej łączenie. Usługa, która została początkowo uruchomiona za pomocą onStartCommand() (przez klienta dzwoniącego pod numer startService()), może nadal otrzymywać połączenia na numer onBind() (gdy klient dzwoni pod numer bindService()).

Dzięki wdrożeniu tych metod możesz monitorować 2 poziomy pętli w cyklu życia usługi:

  • Cały czas trwania usługi występuje między wywołaniem funkcji onCreate() a zwróceniem wartości przez funkcję onDestroy(). Podobnie jak w przypadku aktywności, usługa przechodzi przez początkową konfigurację w czasie onCreate() i zwalnia wszystkie pozostałe zasoby w czasie onDestroy(). Na przykład usługa odtwarzania muzyki może utworzyć wątek, w którym muzyka jest odtwarzana w onCreate(), a potem może zatrzymać ten wątek w onDestroy().

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

  • Czas aktywnego działania usługi rozpoczyna się od wywołania metody onStartCommand() lub onBind(). Każda metoda otrzymuje wartość Intent przekazaną do startService() lub bindService().

    Jeśli usługa jest uruchomiona, jej aktywny okres kończy się w tym samym momencie, w którym kończy się cały okres (usługa jest nadal aktywna, nawet gdy onStartCommand() wraca). Jeśli usługa jest związana, aktywny okres jej trwania kończy się, gdy zwracana jest wartość onUnbind().

Uwaga: chociaż uruchomiona usługa jest zatrzymywana przez wywołanie stopSelf() lub stopService(), nie ma odpowiedniego wywołania zwrotnego dla tej usługi (nie ma wywołania zwrotnego onStop()). Jeśli usługa nie jest powiązana z klientem, system ją niszczy, gdy zostanie zatrzymana. onDestroy() to jedyny otrzymany wywołania zwrotne.

Więcej informacji o tworzeniu usługi, która zapewnia wiązanie, znajdziesz w dokumencie Usługi związane, w którym znajdziesz więcej informacji o metodzie wywołania zwrotnego onRebind() w sekcji Zarządzanie cyklem życia usługi związanej.