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

Android od podstaw: operacje w tle

by
Difficulty:BeginnerLength:MediumLanguages:
This post is part of a series called Android From Scratch.
Android From Scratch: How to Store Application Data Locally
Android From Scratch: Creating Styles and Themes

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

Oprócz najbardziej podstawowych aplikacji na Androida, wszystko co budujesz będzie wymagało w pewnym stopniu wykorzystania wątków w tle do przeprowadzenia operacji. Wszystko dlatego, że Android posiada coś znanego jako ANR (Application Not Responsive), który pojawia się, gdy operacja w wątku UI trwa ponad pięć sekund, uniemożliwiając użytkownikowi wprowadzanie danych i powodując zawieszenie się aplikacji.

Aby tego uniknąć, musisz przenieść dłużej trwające operacje, takie jak żądania sieci lub zapytania do bazy danych, do różnych wątków, aby umożliwić użytkownikowi korzystanie z aplikacji. Chociaż dogłębna analiza wątków jest dużym i złożonym tematem w informatyce, w tym poradniku przedstawię podstawowe pojęcia wątków w systemie Android i niektóre z narzędzi dostępnych, które pomogą ci zbudować aplikacje działające wydajnie dzięki procesom w tle.

Łatwiej uczysz się za pomocą wideo? Obejrzyj nasz kurs:

Omówienie wątków

Po uruchomieniu aplikacji, startuje nowy proces Linuxa z pojedynczym głównym wątkiem wykonywalnym. Posiada on dostęp do zestawu narzędzi UI Androida, nasłuchuje wpisywane przez użytkownika dane, obsługuje rysowanie na ekranie urządzenia Android. W związku z tym, powszechnie określany jest jako wątek UI.

Wszystkie komponenty aplikacji działają domyślnie wewnątrz tego samego wątku i procesu, jednakże można utworzyć dodatkowe wątku w celu odsunięcia zadań od wątku UI i uniknięcia ANR. Jeśli mówimy o wątkach w systemie Android, istnieją dwie proste zasady do zapamiętania, które umożliwiają poprawne funkcjonowanie aplikacji.

  1. Nie blokuj wątku UI.
  2. Nie próbuj uzyskać dostępu do komponentów UI Androida spoza wątku UI.

Chociaż możesz zastosować do pierwszej zasady tworząc nowy Thread i Runnable, poradzenie sobie z drugą regułą będzie trochę bardziej skomplikowane. Przeanalizujmy poniższy fragment kodu:

Powyższy kod nie opóźni wątku UI, gdy nowy wątek ominie ANR, jednakże próba ustawienia obiektu TextView spowoduje, że aplikacja wyrzuci następujący błąd:

Na szczęście, istnieje kilka prostych sposobów na pozbycie się go. Możesz użyć metody runOnUiThread(Runnable), aby wykonać kod z powrotem w głównym wątku aplikacji.

Możesz również wziąć standardowy obiekt View i użyć post na Runnable.

Chociaż obie sztuczki pomogą zabezpieczyć twoje wątki, w miarę budowania coraz bardziej skomplikowanych aplikacji stanie się to bardziej kłopotliwe.

AsyncTask

AsyncTast jest jednym z narzędzi dostępnych w systemie Android pozwalającym na zarządzanie złożonością wątków w tle. AsyncTask dostarcza wątek roboczy do operacji blokujących, a następnie udostępnia wyniki z powrotem w wątku UI ze wstępnie utworzoną metodą wywołania zwrotnego, pozwalając na łatwe wykonywanie zadań bez konieczności bawienia się wątkami i uchwytami.

Cykl życia AsyncTask

Przed rozpoczęciem pracy z klasą AsyncTask, musisz zrozumieć jej cykl życia w porównaniu do operacji w wątku głównym.

AsyncTask Lifecycle

Pierwsza metoda wywoływana przez AsyncTask to onPreExecute(). Uruchamia ona wątek UI oraz ustawia wszystkie komponenty interfejsu potrzebne do zakomunikowania użytkownikowi, że coś się dzieje.

Po zakończeniu onPreExecute(), wywoływana jest doInBackground(T). To ogólna nazwa oznaczająca informację, która musisz przekazać do metody, aby wykonać jej zadanie. Na przykład, jeśli piszesz zadanie, które pobiera JSON z adresu URL, należy przekazać URL do tej metody jako String. Gdy operacja robi postęp w doInBackground(), możesz wywołać onProgressUpdate(T) w celu aktualizacji UI (na przykład paska postępu na ekranie). Tutaj ogólną wartością reprezentującą postęp jest Integer.

Gdy metoda doInBackground() zostanie zakończona, może zwrócić obiekt, który jest przekazywany do onPostExecute(T), na przykład JSONObject pobrany z początkowego adresu URL. onPostExecute(T) działa w wątku UI.

Podczas tworzenia klasy AsyncTask, musisz nadpisać te obiekty zarówno w deklaracji klasy jak i w powyższych metodach. Poniżej znajduje się przykład AsyncTask aktualizującej ProgressBar co sekundę.

Tutaj warto zauważyć, że onPostExecute(T) sprawdza warunek isCancelled(). To dlatego, że istnieje jeden duży problem z AsyncTask: zarządza ona odniesieniem do Context, nawet jeśli Context został zniszczony.

Najlepiej to widać podczas uruchamiania AsyncTask i obracania ekranu. Jeśli spróbujesz odnieść element Context (taki jak View lub Activity) po zniszczeniu oryginalnego Context, wyrzucony zostanie błąd Exception.  Najłatwiejszym sposobem na obejście tego jest wywołanie cancel(true) w AsyncTask w metodzie onDestroy() aktywności lub fragmentu, a następnie sprawdzenie czy zadanie nie zostało anulowane w onPostExecute(T).

Jak wszystko w programowaniu, odpowiedź na pytanie kiedy korzystać z AsyncTask brzmi: to zależy. Proste w użyciu AsyncTasks nie są rozwiązaniem wszystkich kwestii dotyczących wątków, ale zdecydowanie najlepszym dla krótki operacji trwających góra kilka sekund. Jeśli posiadasz operację, która może trwać dłużej, zalecam zainteresowanie się ThreadPoolExecutorService, lub GcmNetworkManager (kompatybilna wstecznie wersja JobScheduler)

Usługi

Kiedy potrzebujesz przeprowadzić długotrwałe operacje w tle, takie jak odtwarzanie muzyki, transakcje sieciowe lub współpraca z dostawcą treści, rozważ wykorzystanie Service. Podstawowy obiekt Service może istnieć w dwóch stanach: uruchomionym i przypisanym:

Uruchomiony Service zostaje rozpoczęty przez komponent w twojej aplikacji i pozostanie aktywny w tle urządzenia, nawet jeśli oryginalny komponent został zniszczony. Kiedy zadanie przeprowadzane przez uruchomiony Service zostanie zakończone, Service zostanie zatrzymany. Standardowo uruchomiony Service jest generalnie wykorzystywany do długotrwałych zadań w tle, które nie wymagają komunikacji z pozostałą częścią aplikacji.

Podobnie działa przypisany Service, który również dostarcza wywołanie zwrotne dla różnych komponentów aplikacji, które mogą się z nim związać. Gdy wszystkie przypisane komponenty odłączą się od Service, zostanie on zatrzymany. Zwróć uwagę, że te dwa sposoby na uruchomienie Service nie wykluczają się wzajemnie—możesz uruchomić Service, który będzie działał bezterminowo i posiadał związane z nim komponenty.

IntentService

Jednym z największych problemów ze standardowym Service jest brak wsparcia dla wielu jednoczesnych żądań, ponieważ byłoby to koszmarem wielowątkowości. Sposobem na obejście tego jest rozszerzenie IntentService, który rozszerza standardowy Service. IntentService tworzy domyślny wątek roboczy do wykonywania wszystkich intencji, które są odbierane w onStartCommand() tak, aby wszystkie operacje były przeprowadzane poza głównym wątkiem. Następnie tworzy kolejkę roboczą służącą do wysyłania po kolei każdej intencji do onHandleIntent(), dzięki czemu nie trzeba martwić się problemami z wielowątkowością.

Oprócz obsługi wątków, IntentService zatrzymuje się automatycznie, gdy wszystkie uruchomione żądania zostały zakończone. Ponieważ wszystkie szczegóły dotyczące implementacji są obsługiwane w IntentService, twoja praca jako deweloper jest dość prosta.

Podsumowanie

W tym poradniku dowiedziałeś się wiele o wątkach i wielowątkowości w systemie Android. Na temat wątków zostały napisane całe książki, ale powinieneś teraz posiadać podstawy do zaprogramowania ogólnych zadań i zrozumieć szczegółową dokumentację dla przyszłych i bardziej złożonych aplikacji na Androida.

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.