Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android SDK

Współbieżność w Androidzie w ramach usługi

by
Difficulty:IntermediateLength:MediumLanguages:

Polish (Polski) translation by Mateusz Kurlit (you can also view the original English article)

W tym poradniku omówimy komponent Service i jego superklasę IntentService. Nauczymy się kiedy i jak korzystać z tego komponentu w celu tworzenia rozwiązań współbieżności dla długotrwałych operacji w tle. Rzucimy również okiem na IPC (Inter Process Communication), aby dowiedzieć się jak komunikować się z usługami działającymi w różnych procesach.

Aby wykorzystać w pełni ten poradnik, powinieneś posiadać podstawową wiedzę o współbieżności w Androidzie. Jeśli nie wiesz za wiele na ten temat, zachęcam najpierw do przeczytania naszych artykułów.

1. Komponent usługi

Komponent Service jest bardzo ważną częścią struktury współbieżności w Androidzie. Realizuje konieczność przeprowadzania długotrwałych operacji w aplikacji lub wyposaża w pewną funkcjonalność inne aplikacje. W tym poradniku, skoncentrujemy się wyłącznie na uruchamianiu długotrwałych zadań w komponencie Service i omówimy jak wykorzystać je w udoskonalaniu współbieżności.

Co to jest usługa?

Service to prosty komponent uruchamiany przez system w celu przeprowadzania długotrwałych operacji, które niekoniecznie zależą od samego użytkownika. Może być niezależny od cyklu życia aktywności oraz działać w całkowicie innym procesie.

Zanim przejdziemy do dyskusji o tym, co reprezentuje Service, warto podkreślić, że usługi są powszechnie wykorzystywane w długotrwałych operacjach tła i przeprowadzaniu zadań na różnych procesach, jednak sam Service nie reprezentuje wątku ani procesu. Będzie tylko działać w wątku tła lub w innym procesie, jeśli wyraźnie zostanie to polecone.

Service posiada dwie główne funkcje:

  • Instrument dla aplikacji, który przekazuje systemowi informację, że chce coś robić w tle.
  • Instrument dla aplikacji, który przedstawia pewną funkcjonalność innym aplikacjom.

Usługi i wątki

Istnieje wiele nieporozumień związanych z usługami i wątkami. Po zadeklarowaniu Service, nie zawiera on Thread. W gruncie rzeczy, domyślnie działa bezpośrednio w głównym wątku i dowolna operacja wykonywana w tym wątku potencjalnie może zawiesić aplikację. (Chyba że użyjemy podklasy IntentService, która zawiera skonfigurowany wątek roboczy.)

Jak więc usługi oferują rozwiązanie współbieżności? Domyślnie Service nie zawiera wątku, ale może być z łatwością skonfigurowany do pracy z własnym lub pulą wątków. Za chwilę dowiemy się więcej na ten temat.

Pomijając brak wbudowanego wątku, Service jest doskonałym rozwiązaniem problemów ze współbieżnością oraz pewnych sytuacji. Oto kilka argumentów przemawiających za wyborem Service w porównaniu do innych rozwiązań współbieżności, takich jak AsyncTask lub struktura HaMeR:

  • Service może być niezależny od cyklów życia aktywności.
  • Service jest odpowiedni do przeprowadzania długotrwałych operacji.
  • Usługi nie zależą od działań użytkownika.
  • Podczas działania w różnych procesach, Android może starać się zachować usługi w pamięci, nawet gdy brakuje zasobów w systemie.
  • Service może być uruchomiony ponownie w celu wznowienia swojej pracy.

Typy usług

Istnieją dwa typy Service, uruchomiony i związany.

Context.startService() rozpoczyna pierwszy typ. Generalnie przeprowadza tylko jedną operację, działa do jej zakończenia, następnie sam się wyłącza. Zwykle nie zwraca żadnych wyników interfejsowi użytkownika.

Context.bindService() rozpoczyna drugi typ i umożliwia dwukierunkową komunikację między klientem i Service. Potrafi również łączyć się z wieloma klientami. Wyłącza się, jeśli żaden klient nie jest do niego podłączony.

Aby móc wybierać między tymi dwoma typami, Service musi zaimplementowane wywołania zwrotne: onStartCommand(), aby działać jako uruchomiona usługa oraz onBind() jako związana. Service może podjąć decyzję o wdrożeniu jednego typu, ale potrafi również bezproblemowo zaimplementować oba.

2. Wdrożenie usługi

Aby użyć usługi, rozszerz klasę Service i nadpisz jej metody wywołania zwrotnego według typu Service. Jak wspomniano wcześniej, należy zaimplementować metodę onStartCommand() dla uruchomionej usługi oraz onBind() dla związanej. Tak naprawdę, metoda onBind() musi być zadeklarowana dla obu typów, ale może zwracać wartość zerową uruchomionym usługom.

  • onStartCommand(): rozpoczęta przez Context.startService(). Zazwyczaj wywoływana z aktywności. Po wywołaniu, usługa może działać w nieskończoność, da się ją zatrzymać wywołując stopSelf() lub stopService().
  • onBind() pojawia się, kiedy komponent chce połączyć się z usługą. Wywoływana w systemie przez Context.bindService(). Zwraca IBinder, który wyświetla interfejs komunikujący się z klientem.

Warto również wziąć pod uwagę cykl życia usługi. Metody onCreate() i onDestroy() powinny być zaimplementowane w celu inicjowania i wyłączania zasobów lub operacji w ramach usługi.

Deklarowanie usługi w manifeście

Komponent Service musi być zadeklarowany w manifeście z elementem <service>. W deklaracji można, ale nie trzeba ustawiać innego procesu dla uruchamiania Service.

2.2. Praca z uruchomionymi usługami

Aby zainicjować uruchomioną usługę, musisz wywołać metodę Context.startService(). Intent należy utworzyć razem z klasami Context i Service. Również wszystkie istotne informacje lub dane powinny zostać przekazane do Intent.

W klasie Service, powinniśmy zająć się metodą onStartCommand(). Należy w niej wywołać konkretną operację, którą chcesz wykonać za pomocą uruchomionej usługi. W ten sposób, przetworzysz Intent, aby odebrać informacje wysłane przez klienta. startId reprezentuje unikalne ID tworzone automatycznie dla tego żądania, a flags może zawierać dodatkowe informacje.

onStartCommand() zwraca stałą int, która kontroluje zachowanie.

  • Service.START_STICKY: usługa jest uruchamiana ponownie, jeśli zostanie zamknięta.
  • Service.START_NOT_STICKY: usługa nie jest uruchamiana ponownie.
  • Service.START_REDELIVER_INTENT: usługa jest uruchamiana ponownie po awarii, a przetwarzane intencje zostają ponownie dostarczone.

Jak wspomniano wcześniej, uruchomioną usługę należy zatrzymać, inaczej będzie działać w nieskończoność. Wystarczy, że w Service wywoła się stopSelf() lub w kliencie stopService().

Łączenie z usługami

Komponenty mogą tworzyć połączenia z usługami, ustanawiając z nimi dwukierunkową komunikację. Klient musi wywołać Context.bindService(), przekazując do Intent, interfejs ServiceConnection i flags jako patrametry. Service może być związany z wieloma klientami i zostanie wyłączony, gdy żaden z nich nie będzie do niego podłączony.

Istnieje możliwość przesyłania obiektów Message do usług. Wystarczy utworzyć Messenger po stronie klienta w implementacji interfejsu ServiceConnection.onServiceConnected i użyć go do przesyłania obiektów Message do Service.

Można również przekazać odpowiedź Messenger do Service, aby klient otrzymywał wiadomości. Ale uważaj, klient może być niedostępny, aby otrzymywać wiadomości usługi. Możesz również użyć BroadcastReceiver lub dowolnego rozwiązania dotyczącego powiadomień.

Ważne, aby rozwiązać połączenie z usługą po wyłączeniu klienta.

Po stronie Service, musisz zaimplementować metodę Service.onBind(), dostarczając IBinder za pośrednictwem Messenger. W ten sposób przekażesz odpowiedź Handler do obsługi obiektów Message otrzymanych od klienta.

3. Współbieżność w ramach usług

Nadszedł czas, aby omówić rozwiązywanie problemów ze współbieżnością w ramach usług. Jak wspomniano wcześniej, standardowy Service nie zawiera żadnych dodatkowych wątków i domyślnie działa w głównym Thread. Aby pozbyć się tego problemu, musisz dodać roboczy Thread, pulę wątków lub wykonać Service w innym procesie. Możesz również skorzystać z podklasy o nazwie IntentService, która już zawiera Thread.

Uruchamianie usług w wątku roboczym

Aby wykonać Service w Thread tła, wystarczy stworzyć dodatkowy Thread i uruchomić tam zadanie. Jednakże, Android oferuje nam lepsze rozwiązanie. Jednym z najlepszych sposobów na wykorzystanie systemu jest zaimplementowanie struktury HaMeR wewnątrz Service, na przykład zapętlając Thread za pomocą kolejki komunikatów, która może przetwarzać wiadomości w nieskończoność.

Warto zrozumieć, że ta implementacja przetworzy zadania sekwencyjnie. Jeśli chcesz jednocześnie otrzymywać i przetwarzać wiele zadań, powinieneś użyć puli wątków. Kwestia wykorzystania puli wątków nie wchodzi w zakres tego poradnika.

Aby użyć HaMeR musisz dostarczyć Service z Looper, Handler i HandlerThread.

Jeśli nie znasz struktury HaMeR, przeczytaj nasze poradniki o HaMeR dla współbieżności w Androidzie.

IntentService

Jeśli nie ma potrzeby, aby Service działał przez dłuższy czas, możesz użyć podklasy IntentService, która potrafi uruchamiać zadania wewnątrz wątków tła. Tak naprawdę, IntentService to Service z implementacją bardzo podobną do zaproponowanej powyżej.

Aby użyć tej klasy, wystarczy ją rozszerzyć i zaimplementować metodę onHandleIntent(), która będzie wywoływana za każdym razem, gdy klient wezwie startService() w Service. Należy zwrócić uwagę, że IntentService zostanie zatrzymana zaraz po zakończeniu zadania.

IPC (Inter Process Communication)

Service może działać w całkowicie innym Process, niezależnie od wszystkich zadań znajdujących się w głównym wątku. Proces posiada własny przydział pamięci, grupę wątków oraz priorytety przetwarzania. Takie podejście może być bardzo przydatne, gdy trzeba działać niezależnie od głównego procesu.

Komunikacja między różnymi procesami nosi nazwę IPC (Inter Process Communication). W Service istnieją dwa sposoby na wykonanie IPC: wykorzystując Messenger lub implementując interfejs AIDL.

Nauczyliśmy się jak wysyłać i odbierać wiadomości między usługami. Wszystko co należy zrobić to stworzyć Messenger za pomocą instancji IBinder otrzymaną podczas połączenia i wykorzystać go do przesłania odpowiedzi obiektowi Messenger z powrotem do Service.

Interfejs AIDL jest bardzo rozbudowany oraz umożliwiający bezpośrednie wywoływanie metod Service działających w oddzielnych procesach i warto skorzystać z niego, gdy mamy do czynienia ze złożonym Service. AIDL jest bardzo trudny w implementacji oraz rzadko wykorzystywany, dlatego nie zostanie omówiony w tym poradniku.

4. Podsumowanie

Usługi mogą być proste lub złożone. Wszystko zależy od wymagań danej aplikacji. W tym poradniku, starałem się wyjaśnić jak najwięcej kwestii, jednakże skupiłem się tylko na wykorzystaniu usług w celach współbieżności, ale istnieje więcej możliwości dla tego komponentu. Jeśli chcesz poszerzyć wiedzę na ten temat, przejrzyj dokumentację i poradniki Androida.

Do zobaczenia wkrótce!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.