A Service
jest komponentem aplikacji, który może wykonywać długotrwałe operacje w tle. Nie zapewnia ona interfejsu użytkownika. Po uruchomieniu, 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 wejść z nią w interakcję, a nawet wykonać 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 lub współdziałać z dostawcą treści, a wszystko to w tle.
Uwaga: Usługa działa w głównym wątku swojego procesu hostującego; usługa nie tworzy własnego wątku i nie działa w oddzielnym procesie, chyba że określisz inaczej. Powinieneś uruchomić wszelkie operacje blokujące na oddzielnym wątku w ramach usługi, aby uniknąć błędów ApplicationNot Responding (ANR).
- Typy usług
- Wybór między usługą a wątkiem
- Podstawy
- Deklarowanie usługi w manifeście
- Tworzenie uruchomionej usługi
- Rozszerzenie klasy Service
- Kotlin
- Java
- Uruchamianie usługi
- Kotlin
- Java
- Zatrzymywanie usługi
- Tworzenie usługi związanej
- Wysyłanie powiadomień do użytkownika
- Zarządzanie cyklem życia usługi
- Implementowanie wywołań zwrotnych cyklu życia
- Kotlin
- Java
Typy usług
Oto trzy różne typy usług:
Na pierwszym planie
Usługa na pierwszym planie wykonuje jakąś operację, która jest zauważalna dla użytkownika. Na przykład aplikacja audio używałaby usługi pierwszoplanowej do odtwarzania ścieżki dźwiękowej. Usługi pierwszoplanowe muszą wyświetlać powiadomienie. Usługi pierwszoplanowe działają nawet wtedy, gdy użytkownik nie wchodzi w interakcję z aplikacją.
Gdy używasz usługi pierwszoplanowej, musisz wyświetlić powiadomienie, aby użytkownicy byli aktywnie świadomi, że usługa jest uruchomiona. Powiadomienie to nie może zostać odrzucone, chyba że usługa zostanie zatrzymana lub usunięta z pierwszego planu.
Dowiedz się więcej o tym, jak skonfigurować usługi pierwszoplanowe w swojej aplikacji.
Uwaga: API WorkManager oferuje elastyczny sposób planowania zadań i jest w stanie uruchomić te zadania jako usługi pierwszoplanowe, jeśli zajdzie taka potrzeba. W wielu przypadkach użycie WorkManagera jest lepsze niż bezpośrednie użycie usług pierwszoplanowych.
Tło Usługa tła wykonuje operację, która nie jest bezpośrednio zauważana przez użytkownika. Na przykład, jeśli aplikacja używa usługi do kompaktowania pamięci masowej, to zazwyczaj jest to usługa tła.
Uwaga: Jeśli Twoja aplikacja obsługuje API na poziomie 26 lub wyższym, system nakłada ograniczenia na uruchamianie usług w tle, gdy sama aplikacja nie jest na pierwszym planie. W większości sytuacji, na przykład, nie powinieneś uzyskiwać dostępu do informacji o lokalizacji z tła. Zamiast tego należy zaplanować zadania za pomocą programu WorkManager.
Związanie Usługa jest związana, gdy komponent aplikacji wiąże się z nią przez wywołaniebindService()
. Powiązana usługa oferuje interfejs klient-serwer, który umożliwia komponentom interakcję z usługą, wysyłanie żądań, odbieranie wyników, a nawet robienie tego między procesami za pomocą komunikacji międzyprocesowej (IPC). Powiązana usługa działa tylko tak długo, jak długo inny komponent aplikacji jest z nią powiązany. Wiele komponentów może wiązać się z usługą jednocześnie, ale gdy wszystkie się rozłączą, usługa jest niszczona.
Pomimo że ta dokumentacja omawia oddzielnie usługi uruchomione i związane, twoja usługa może działać na oba sposoby – może być uruchomiona (działać bez końca), a także pozwalać na wiązanie. Jest to po prostu kwestia tego, czy zaimplementujesz kilka metod wywołania zwrotnego: onStartCommand()
, aby umożliwić komponentom jej uruchamianie, oraz onBind()
, aby umożliwić wiązanie.
Niezależnie od tego, czy Twoja usługa jest uruchamiana, wiązana, czy obie, dowolny komponent aplikacji może korzystać z usługi (nawet z oddzielnej aplikacji) w taki sam sposób, w jaki dowolny komponent może korzystać z aktywności – uruchamiając ją za pomocą Intent
. Można jednak zadeklarować usługę jako prywatną w pliku manifestu i zablokować dostęp innym aplikacjom.Jest to omówione bardziej szczegółowo w sekcji Deklarowanie usługi w ichanifeście.
Wybór między usługą a wątkiem
Usługa jest po prostu komponentem, który może działać w tle, nawet gdy użytkownik nie wchodzi w interakcję z twoją aplikacją, więc powinieneś utworzyć usługę tylko wtedy, gdy tego właśnie potrzebujesz.
Jeśli musisz wykonać pracę poza swoim głównym wątkiem, ale tylko wtedy, gdy użytkownik wchodzi w interakcję z twoją aplikacją, powinieneś zamiast tego utworzyć nowy wątek w kontekście innego komponentu aplikacji. Na przykład, jeśli chcesz odtworzyć muzykę, ale tylko podczas działania, możesz utworzyć wątek w onCreate()
, zacząć go uruchamiać w onStart()
i zatrzymać w onStop()
.Rozważ również użycie puli wątków i executorów z pakietu java.util.concurrent
lub Kotlin coroutines zamiast tradycyjnej klasyThread
. Zobacz dokument Threading on Android, aby uzyskać więcej informacji o przenoszeniu wykonywania do wątków tła.
Pamiętaj, że jeśli używasz usługi, nadal domyślnie działa ona w głównym wątku aplikacji, więc nadal powinieneś tworzyć nowy wątek w ramach usługi, jeśli wykonuje ona intensywne lub blokujące operacje.
Podstawy
Aby utworzyć usługę, musisz utworzyć podklasę klasy Service
lub użyć jednej z jej istniejących podklas. W swojej implementacji musisz nadpisać niektóre metody wywołania zwrotnego, które obsługują kluczowe aspekty cyklu życia usługi i zapewniają mechanizm, który pozwala komponentom tobind do usługi, jeśli jest to właściwe. Oto najważniejsze metody wywołania zwrotnego, które powinieneś nadpisać:
onStartCommand()
System wywołuje tę metodę przez wywołaniestartService()
, gdy inny komponent (taki jak aktywność) żąda uruchomienia usługi.Gdy ta metoda wykonuje się, usługa jest uruchomiona i może działać in thebackground w nieskończoność. Jeśli zaimplementujesz to, twoim obowiązkiem jest zatrzymanie usługi, gdy jej praca zostanie zakończona przez wywołaniestopSelf()
lubstopService()
. Jeśli chcesz tylko zapewnić wiązanie, nie musisz implementować tej metody.onBind()
System wywołuje tę metodę przez wywołaniebindService()
, gdy inny komponent chce związać się z usługą (na przykład w celu wykonania RPC).W swojej implementacji tej metody musisz zapewnić interfejs, którego klienci używają do komunikacji z usługą, zwracającIBinder
. Zawsze musisz zaimplementować tę metodę; jeśli jednak nie chcesz zezwolić na wiązanie, powinieneś zwrócić wartość returnnull.onCreate()
System wywołuje tę metodę w celu wykonania jednorazowych procedur konfiguracyjnych, gdy usługa jest początkowo tworzona (przed wywołaniem metodonStartCommand()
lubonBind()
). Jeżeli 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 to zaimplementować, aby wyczyścić wszelkie zasoby, takie jak wątki, zarejestrowane słuchacze lub odbiorniki. Jest to ostatnie wywołanie, które otrzymuje usługa.
Jeśli komponent uruchamia usługę przez wywołanie startService()
(co skutkuje wywołaniem onStartCommand()
), usługa działa do czasu, aż sama się zatrzyma za pomocą stopSelf()
lub inny komponent zatrzyma ją przez wywołanie stopService()
.
Jeśli komponent wywoła bindService()
w celu utworzenia usługi, a onStartCommand()
nie zostanie wywołany, usługa działa tak długo, jak długo komponent jest z nią związany. Po odłączeniu usługi od wszystkich jej klientów, system ją niszczy.
System Android zatrzymuje usługę tylko wtedy, gdy pamięć jest mała i musi odzyskać zasoby systemowe dla aktywności, która ma fokus użytkownika. Jeśli usługa jest powiązana z aktywnością, która ma fokus użytkownika, jest mniej prawdopodobne, że zostanie zabita; jeśli usługa jest zadeklarowana do działania na pierwszym planie, rzadko jest zabijana.Jeśli usługa jest uruchomiona i długo działa, system obniża jej pozycję na liście zadań w tle w czasie, a usługa staje się bardzo podatna na tokilling – jeśli twoja usługa jest uruchomiona, musisz zaprojektować ją tak, aby z wdziękiem obsługiwała ponowne uruchomienie przez system. Jeśli system zabije twoją usługę, uruchomi ją ponownie, gdy tylko zasoby staną się dostępne, ale to również zależy od wartości, którą zwrócisz z onStartCommand()
. Więcej informacji na temat tego, kiedy system może zniszczyć usługę, można znaleźć w dokumencie Processes and Threading.
W następnych sekcjach zobaczysz, jak możesz tworzyć metodystartService()
ibindService()
service, a także jak używać ich z innych komponentów aplikacji.
Deklarowanie usługi w manifeście
Musisz zadeklarować wszystkie usługi w pliku manifestu swojej aplikacji, tak jak robisz to dla aktywności i innych komponentów.
Aby zadeklarować swoją usługę, dodaj element <service>
jako dziecko elementu <application>
. Oto przykład:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application></manifest>
Zobacz odnośnik do elementu <service>
, aby uzyskać więcej informacji na temat deklarowania usługi w manifeście.
Istnieją inne atrybuty, które możesz zawrzeć w elemencie <service>
w celu określenia właściwości, takich jak uprawnienia wymagane do uruchomienia usługi i proces, w którym usługa powinna działać. Atrybut android:name
jest jedynym wymaganym atrybutem – określa on nazwę klasy usługi. Po opublikowaniu aplikacji pozostaw tę nazwę niezmienioną, aby uniknąć ryzyka złamania kodu z powodu zależności od jawnych intencji uruchomienia lub powiązania usługi (przeczytaj wpis na blogu, ThingsThat Cannot Change).
Uwaga: Aby zapewnić, że Twoja aplikacja jest bezpieczna, zawsze używaj jawnej intencji podczas uruchamiania Service
i nie deklaruj filtrów intencji dla swoich usług. Użycie niejawnego zamiaru do uruchomienia usługi stanowi zagrożenie bezpieczeństwa, ponieważ nie można być pewnym usługi, która odpowiada na zamiar, a użytkownik nie może zobaczyć, która usługa zostanie uruchomiona. Począwszy od Androida 5.0 (poziom API 21), system rzuca wyjątek, jeśli wywołaszbindService()
z niejawnym zamiarem.
Możesz zapewnić, że twoja usługa jest dostępna tylko dla twojej aplikacji, włączając atrybut android:exported
i ustawiając go na false
. To skutecznie powstrzymuje inne aplikacje przed uruchomieniem twojej usługi, nawet jeśli używasz jawnego zamiaru.
Uwaga: Użytkownicy mogą zobaczyć, 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ą zatrzymać. Aby uniknąć przypadkowego zatrzymania usługi przez użytkowników, należy dodać atrybut android:description
do elementu <service>
w manifeście aplikacji. W opisie podaj krótkie zdanie wyjaśniające, co robi usługa i jakie korzyści zapewnia.
Tworzenie uruchomionej usługi
Uruchomiona usługa to taka, którą inny komponent uruchamia przez wywołanie startService()
, co powoduje wywołanie metodyonStartCommand()
usługi.
Gdy usługa jest 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ł, zostanie zniszczony. Jako taka, usługa powinna się zatrzymać, gdy jej zadanie zostanie zakończone przez wywołanie stopSelf()
lub inny komponent może ją zatrzymać przez wywołanie stopService()
.
Komponent aplikacji, taki jak aktywność, może uruchomić usługę przez wywołanie startService()
i przekazanie Intent
, która określa usługę i zawiera wszelkie dane do wykorzystania przez usługę. Usługa otrzymuje tę Intent
w metodzie onStartCommand()
.
Na przykład załóżmy, że aktywność musi zapisać pewne dane do internetowej bazy danych. Aktywność może uruchomić usługę towarzyszącą i dostarczyć jej dane do zapisania, przekazując intencję do startService()
. Usługa odbiera intencję w 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 jest domyślnie uruchamiana w tym samym procesie co aplikacja, w której została zadeklarowana, i w głównym wątku tej aplikacji. Jeśli usługa wykonuje intensywne lub blokujące operacje, podczas gdy użytkownik wchodzi w interakcję z działaniem z tej samej aplikacji, usługa spowalnia wydajność działania. Aby uniknąć wpływu na wydajność aplikacji, należy rozpocząć nowy wątek wewnątrz usługi.
Klasa Service
jest klasą bazową dla wszystkich usług. Kiedy 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 twojej aplikacji, co może spowolnić działanie każdej aktywności, którą twoja aplikacja wykonuje.
Szkielet Androida zapewnia również IntentService
podklasę Service
, która używa wątku roboczego do obsługi wszystkich żądań uruchomienia, jedno po drugim. Używanie tej klasy nie jest zalecane dla nowych aplikacji, ponieważ nie będzie ona działać dobrze od Androida 8 Oreo, ze względu na wprowadzenie limitów wykonania Background.Ponadto jest ona przestarzała od Androida 11.Możesz użyć JobIntentService jako zamiennika dla IntentService
, który jest kompatybilny z nowszymi wersjami Androida.
Następne sekcje opisują, jak możesz zaimplementować własną usługę, jednak powinieneś mocno rozważyć użycie WorkManagera zamiast tego dla większości przypadków użycia. Skonsultuj się z przewodnikiem po przetwarzaniu w tle w systemie Android, aby sprawdzić, czy istnieje rozwiązanie, które pasuje do twoich potrzeb.
Rozszerzenie klasy Service
Możesz rozszerzyć klasę Service
, aby obsłużyć każdą przychodzącą intencję. 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 połączenia w onStartCommand()
i przesyła pracę do Handler
działającego w wątku tła. Działa on tak samo jak IntentService
i przetwarza wszystkie żądania szeregowo, jedno po drugim.Możesz zmienić kod, aby uruchomić pracę na puli wątków, na przykład, jeśli chcesz uruchomić wiele żądań jednocześnie.
Zauważ, że metoda onStartCommand()
musi zwrócić liczbę całkowitą. Liczba całkowita jest wartością, która opisuje, w jaki sposób system powinien kontynuować usługę w przypadku, gdy system ją zabije. Wartość zwracana przez metodę onStartCommand()
musi być jedną z następujących stałych:
START_NOT_STICKY
Jeśli system zabije usługę po zwróceniu wartościonStartCommand()
, nie należy ponownie tworzyć usługi, chyba że istnieją oczekujące żądania do dostarczenia. Jest to najbezpieczniejsza opcja, aby uniknąć uruchamiania usługi, gdy nie jest to konieczne i gdy twoja aplikacja może po prostu ponownie uruchomić wszelkie niedokończone zadania.START_STICKY
Jeśli system zabije usługę po powrocieonStartCommand()
, odtwórz usługę i wywołajonStartCommand()
, ale nie dostarczaj ponownie ostatniego zamiaru.Zamiast tego system wywołujeonStartCommand()
z pustym zamiarem, chyba że istnieją oczekujące zamiary do uruchomienia usługi. W takim przypadku te intencje są dostarczane. Jest to odpowiednie dla odtwarzaczy multimedialnych (lub podobnych usług), które nie wykonują poleceń, ale działają w nieskończoność i czekają na zadanie.START_REDELIVER_INTENT
Jeżeli system zabije usługę po powrocieonStartCommand()
, odtwórz usługę i wywołajonStartCommand()
z ostatnim intencją, która została dostarczona do usługi. Wszystkie oczekujące intencje są dostarczane po kolei. Jest to odpowiednie dla usług, które aktywnie wykonują zadanie, które powinno być natychmiast wznowione, takie jak pobieranie pliku.
Aby uzyskać więcej szczegółów na temat tych wartości zwracanych, zobacz powiązaną dokumentację dla każdej stałej.
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 jej Intent
, która określa, którą usługę należy uruchomić.
Uwaga: Jeśli Twoja aplikacja jest przeznaczona na poziom API 26 lub wyższy, system nakłada ograniczenia na używanie lub tworzenie usług tła, chyba że sama aplikacja jest na pierwszym planie. Jeśli aplikacja musi utworzyć usługę pierwszoplanową, powinna zadzwonić pod numer startForegroundService()
. Metoda ta tworzy usługę działającą w tle, lecz sygnalizuje systemowi, że usługa będzie promować się na pierwszy plan. Po utworzeniu usługi musi ona wywołać swoją metodę startForeground()
w ciągu pięciu sekund.
Na przykład aktywność może uruchomić przykładową usługę z poprzedniej sekcji (HelloService
) za pomocą jawnego zamiaru z startService()
, jak pokazano tutaj:
Kotlin
Intent(this, HelloService::class.java).also { intent -> startService(intent)}
Java
Intent intent = new Intent(this, HelloService.class);startService(intent);
Metoda startService()
zwraca się natychmiast, a system Android wywołuje metodę onStartCommand()
usługi. Jeśli usługa nie jest jeszcze uruchomiona, system najpierw wywołuje onCreate()
, a następnie wywołujeonStartCommand()
.
Jeśli usługa nie zapewnia również wiązania, intencja dostarczona z startService()
jest jedynym sposobem 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 broadcast (z getBroadcast()
) i dostarczyć go do usługi w Intent
, który uruchamia usługę. Usługa może następnie użyć rozgłaszania do dostarczenia wyniku.
Wielokrotne żądania uruchomienia usługi powodują wielokrotne odpowiadające im wywołania doonStartCommand()
usługi. Jednak tylko jedno żądanie zatrzymania usługi (z stopSelf()
lub stopService()
) jest wymagane do jej zatrzymania.
Zatrzymywanie usługi
Uruchomiona usługa musi zarządzać swoim własnym cyklem życia. Oznacza to, że system nie zatrzymuje usługi, chyba że musi odzyskać pamięć systemową, a usługa jest nadal uruchamiana po powrocie onStartCommand()
. Usługa musi zatrzymać się sama przez wywołanie stopSelf()
lub inny komponent może ją zatrzymać przez wywołanie stopService()
.
Po zażądaniu zatrzymania za pomocą stopSelf()
lub stopService()
system niszczy usługę tak szybko, jak to możliwe.
Jeśli twoja usługa obsługuje wiele żądań do onStartCommand()
jednocześnie, nie powinieneś zatrzymywać usługi po zakończeniu przetwarzania żądania startu, ponieważ mogłeś otrzymać nowe żądanie startu (zatrzymanie na końcu pierwszego żądania spowodowałoby zakończenie drugiego). Aby uniknąć tego problemu, możesz użyć stopSelf(int)
, aby zapewnić, że twoje żądanie zatrzymania usługi jest zawsze oparte na ostatnim żądaniu startu. To znaczy, gdy wywołujesz stopSelf(int)
, przekazujesz identyfikator żądania startu (startId
dostarczonego do onStartCommand()
), na które odpowiada twoje żądanie zatrzymania. Wówczas, jeśli usługa otrzyma nowe żądanie startu, zanim zdążysz wywołać stopSelf(int)
, identyfikator nie będzie pasował i usługa nie zatrzyma się.
Uwaga: Aby uniknąć marnowania zasobów systemowych i zużywania energii baterii, upewnij się, że Twoja aplikacja zatrzymuje swoje usługi po zakończeniu pracy.W razie potrzeby inne komponenty mogą zatrzymać usługę przez wywołanie stopService()
. Nawet jeśli włączysz wiązanie dla usługi,musisz zawsze sam zatrzymać usługę, jeśli kiedykolwiek otrzyma ona wywołanie onStartCommand()
.
Więcej informacji na temat cyklu życia usługi można znaleźć w poniższej sekcji Zarządzanie cyklem życia usługi.
Tworzenie usługi związanej
Usługa związana to taka, która pozwala komponentom aplikacji wiązać się z nią przez wywołanie bindService()
w celu utworzenia długotrwałego połączenia.Na ogół nie pozwala ona komponentom uruchamiać jej przez wywołanie startService()
.
Utwórz usługę związaną, gdy chcesz wchodzić w interakcje z usługą z poziomu działań i innych komponentów w swojej aplikacji lub ujawnić część funkcjonalności aplikacji innym aplikacjom za pośrednictwem komunikacji międzyprocesowej (IPC).
Aby utworzyć usługę związaną, zaimplementuj metodę onBind()
wywołania zwrotnego, aby zwrócić IBinder
, która definiuje interfejs do komunikacji z usługą. Inne komponenty aplikacji mogą następnie wywołaćbindService()
, aby pobrać interfejs i rozpocząć wywoływanie metod w usłudze. Usługa żyje tylko po to, by służyć komponentowi aplikacji, który jest z nią związany, więc gdy nie ma komponentów związanych z usługą, system ją niszczy. Nie musisz zatrzymywać związanej usługi w taki sam sposób, w jaki musisz to robić, gdy usługa jest uruchamiana przez onStartCommand()
.
Aby utworzyć związaną usługę, musisz zdefiniować interfejs określający sposób, w jaki klient może komunikować się z usługą. Ten interfejs między usługą a klientem musi być implementacją IBinder
i jest tym, co twoja usługa musi zwrócić z metody onBind()
wywołania zwrotnego. Po otrzymaniu przez klienta IBinder
, może on rozpocząć interakcję z usługą za pośrednictwem tego interfejsu.
Wielu klientów może jednocześnie wiązać się z usługą. Gdy klient kończy interakcję z usługą, wywołuje unbindService()
, aby ją odłączyć.Gdy nie ma klientów związanych z usługą, system niszczy usługę.
Istnieje wiele sposobów implementacji usługi związanej, a implementacja jest bardziej skomplikowana niż w przypadku usługi uruchomionej. Z tych powodów omówienie usługi związanej pojawia się w oddzielnym dokumencie o usługach związanych.
Wysyłanie powiadomień do użytkownika
Gdy usługa jest uruchomiona, może powiadamiać użytkownika o zdarzeniach za pomocą powiadomień Toast lub powiadomień paska stanu.
Powiadomienie Toast to komunikat, który pojawia się na powierzchni bieżącego okna tylko na chwilę przed zniknięciem. Powiadomienie paska stanu dostarcza ikonę na pasku stanu z wiadomością, którą użytkownik może wybrać w celu podjęcia działania (takiego jak rozpoczęcie aktywności).
Zwykle, powiadomienie paska stanu jest najlepszą techniką do użycia, gdy praca w tle, taka jak pobieranie pliku, została zakończona, a użytkownik może teraz na niej działać. Gdy użytkownik wybierze powiadomienie z widoku rozwiniętego, powiadomienie może rozpocząć działanie (takie jak wyświetlenie pobranego pliku).
Zobacz przewodniki deweloperów Powiadomienia Toast lub Powiadomienia paska stanu, aby uzyskać więcej informacji.
Zarządzanie cyklem życia usługi
Cykl życia usługi jest znacznie prostszy niż cykl życia działania. Jednak jeszcze ważniejsze jest zwrócenie uwagi na to, jak usługa jest tworzona i niszczona, ponieważ może ona działać w tle bez wiedzy użytkownika.
Krąg życia usługi – od momentu jej utworzenia do zniszczenia – może przebiegać jedną z dwóch ścieżek:
- Uruchomiona usługa
Usługa jest tworzona, gdy inny komponent wywołuje
startService()
. Usługa działa wtedy w nieskończoność i musi się sama zatrzymać przez wywołaniestopSelf()
. Inny komponent może również zatrzymać usługę przez wywołaniestopService()
. Gdy usługa jest zatrzymana, system ją niszczy. - Usługa związana
Usługa jest tworzona, gdy inny komponent (klient) wywołuje
bindService()
. Następnie klient komunikuje się z usługą za pośrednictwem interfejsuIBinder
. Klient może zamknąć połączenie wywołaniemunbindService()
. Wielu klientów może związać się z tą samą usługą, a gdy wszyscy oni się rozłączą, system niszczy usługę. Usługa nie musi się zatrzymywać.
Te dwie ścieżki nie są całkowicie odrębne. Możesz powiązać się z usługą, która jest już uruchomiona za pomocą startService()
. Na przykład można uruchomić usługę muzyki w tle, wywołując startService()
z Intent
, które identyfikuje muzykę do odtworzenia. Później, być może gdy użytkownik chce sprawować jakąś kontrolę nad odtwarzaczem lub uzyskać informacje o bieżącym utworze, aktywność może związać się z usługą przez wywołanie bindService()
. W przypadkach takich jak ten, stopService()
lub stopSelf()
w rzeczywistości nie zatrzymuje usługi, dopóki wszyscy klienci się nie rozłączą.
Implementowanie wywołań zwrotnych cyklu życia
Podobnie jak aktywność, usługa posiada metody wywołań zwrotnych cyklu życia, które można zaimplementować w celu monitorowania zmian w stanie usługi i wykonywania pracy w odpowiednim czasie. Poniższa usługa szkieletowa 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 () { // The service is being created } override fun (intent: Intent?, flags: Int, startId: Int): Int { // The service is starting, due to a call to startService() return startMode } override fun (intent: Intent): IBinder? { // A client is binding to the service with bindService() return binder } override fun (intent: Intent): Boolean { // All clients have unbound with unbindService() return allowRebind } override fun (intent: Intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } override fun () { // 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 () { // The service is being created } @Override public int (Intent intent, int flags, int startId) { // The service is starting, due to a call to return startMode; } @Override public IBinder (Intent intent) { // A client is binding to the service with return binder; } @Override public boolean (Intent intent) { // All clients have unbound with return allowRebind; } @Override public void (Intent intent) { // A client is binding to the service with , // after onUnbind() has already been called } @Override public void () { // The service is no longer used and is being destroyed }}
Uwaga: W przeciwieństwie do metod wywołania zwrotnego cyklu życia aktywności, nie jest wymagane wywoływanie implementacji nadklasy tych metod wywołania zwrotnego.
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 przedstawia cykl życia, gdy usługa jest tworzona za pomocą bindService()
.
Rysunek 2 ilustruje typowe metody wywołania zwrotnego dla usługi. Chociaż rysunek oddziela usługi tworzone przez startService()
od tych tworzonych przez bindService()
, należy pamiętać, że każda usługa, bez względu na to, jak została uruchomiona, może potencjalnie umożliwiać klientom wiązanie się z nią. Usługa, która została początkowo uruchomiona za pomocą onStartCommand()
(przez klienta wywołującego startService()
), może nadal otrzymywać wywołania onBind()
(gdy klient wywoła bindService()
).
Wdrażając te metody, można monitorować te dwie zagnieżdżone pętle cyklu życia usługi:
- Cały czas życia usługi występuje między czasem, w którym
onCreate()
jest wywoływany, a czasem, w którymonDestroy()
powraca. Podobnie jak aktywność, usługa wykonuje swoją początkową konfigurację wonCreate()
i zwalnia wszystkie pozostałe zasoby wonDestroy()
. Na przykład usługa odtwarzania muzyki może utworzyć wątek, w którym odtwarzana jest muzyka, wonCreate()
, a następnie może zatrzymać ten wątek wonDestroy()
.Uwaga: Metody
onCreate()
ionDestroy()
są wywoływane dla wszystkich usług, niezależnie od tego, czy zostały utworzone przezstartService()
czybindService()
. - Aktywny czas życia usługi rozpoczyna się od wywołania metody
onStartCommand()
lubonBind()
.Każda metoda przekazujeIntent
, która została przekazana dostartService()
lubbindService()
.Jeśli usługa jest uruchomiona, aktywny czas życia kończy się w tym samym momencie, w którym kończy się cały czas życia (usługa jest nadal aktywna nawet po powrocie
onStartCommand()
). Jeśli usługa jest związana, aktywny czas życia kończy się po powrocieonUnbind()
.
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()
). O ile usługa nie jest związana z klientem, system niszczy ją, gdy usługa jest zatrzymana – onDestroy()
jest jedynym otrzymanym wywołaniem zwrotnym.
Więcej informacji na temat tworzenia usługi zapewniającej wiązanie można znaleźć w dokumencie Bound Services, który zawiera więcej informacji na temat metody onRebind()
callback w sekcji Managing the lifecycle ofa bound service.