Zrozumienie rdzenia Androida: Looper, Handler i HandlerThread

zrozumienie rdzenia Androida: Looper, Handler i HandlerThread

Ten artykuł dotyczy Androida Looper, Handler i HandlerThread. Są to jedne z elementów składowych systemu operacyjnego Android.

z własnego doświadczenia do niedawna używałem ich w bardzo ograniczonym kontekście. Mój przypadek użycia polegał na wysyłaniu zadań do głównego wątku/interfejsu użytkownika, głównie w celu aktualizacji interfejsu użytkownika z dowolnego innego wątku. Inne aspekty operacji wielowątkowej były obsługiwane za pomocą alternatywnych sposobów, takich jak ThreadPoolExecutor, IntentService i AsyncTask.

wielowątkowość i uruchamianie zadań to stare tematy. Sama Java posiada Javę.util.współbieżny pakiet i Fork/Join framework, aby go ułatwić. Kilka bibliotek zostało napisanych w celu usprawnienia operacji asynchronicznych. RxJava jest obecnie najpopularniejszą biblioteką do programowania reaktywnego i projektowania aplikacji asynchronicznych.

więc dlaczego piszę o starej szkole?

Looper, Handler, i HandlerThread to sposób na rozwiązanie problemów programowania asynchronicznego na Androida. Nie są one starą szkołą, ale zgrabną strukturą, na której zbudowany jest złożony framework Androida.

dla nowych programistów, jest wysoce zalecane, aby zrozumieć zasady stojące za nimi i doświadczonych należy powrócić do tego tematu, aby przypomnieć sobie drobne szczegóły.

stworzyłem również samouczek wideo na ten temat i Gorąco polecam go obejrzeć. Kliknij tutaj, aby obejrzeć teraz.

Przypadki Użycia:

  1. główny wątek w Androidzie jest zbudowany z Looper i Handlers. Tak więc zrozumienie tego jest niezbędne do stworzenia odblokowanego responsywnego interfejsu użytkownika.
  2. programiści piszący biblioteki nie mogą sobie pozwolić na korzystanie z bibliotek innych firm ze względu na ich rozmiar. Tak więc dla nich najlepszą opcją jest wykorzystanie istniejącego dostępnego zasobu. Pisanie własnego rozwiązania dla niego nie zawsze może uzyskać taki poziom wydajności i optymalizacji.
  3. ten sam argument można również zastosować do firm/osób fizycznych wysyłających zestawy SDK. Klienci mogą mieć różne implementacje, ale wszystkie z nich będą miały wspólne interfejsy API android framework.
  4. pełne zrozumienie ich zwiększy zdolność do śledzenia zestawów SDK dla Androida i klas pakietów w ogóle.

zacznijmy eksplorację/rewizję od kwestionariusza.

oczekuję, że czytelnik będzie miał podstawową wiedzę na temat wątków Javy. Jeśli potrzebujesz, a następnie uzyskać szybki przegląd wątku java i Runnable.

jaki jest problem z wątkiem java?

wątki Javy są jednorazowego użytku i umierają po wykonaniu metody run.

Czy możemy to poprawić?

nić to miecz obosieczny. Możemy przyspieszyć wykonanie, rozdzielając zadania między wątki wykonania, ale możemy również spowolnić je, gdy wątki są w nadmiarze. Tworzenie wątku samo w sobie jest narzutem. Tak więc najlepszą opcją jest posiadanie optymalnej liczby wątków i ponowne wykorzystanie ich do wykonywania zadań.

Model do wielokrotnego użytku nici:

  1. wątek jest utrzymywany przy życiu, w pętli za pomocą metody run().
  2. zadanie jest wykonywane seryjnie przez ten wątek i jest utrzymywane w kolejce (MessageQueue).
  3. wątek musi zostać zakończony po zakończeniu.

jaki jest sposób na Androida?

powyższy model jest zaimplementowany w systemie Android przez Looper, Handler i HandlerThread. System można zwizualizować jako pojazd, jak w okładce artykułu.

  1. MessageQueue jest kolejką, która ma zadania zwane wiadomościami, które powinny być przetworzone.
  2. Handler wykonuje zadanie w MessageQueue używając Looper, a także wykonuje je, gdy zadanie wychodzi z MessageQueue.
  3. Looper jest workerem, który utrzymuje wątek przy życiu, pętli przez MessageQueue i wysyła wiadomości do odpowiedniego handler do przetworzenia.
  4. w końcu Thread zostaje zakończona przez wywołanie metody Looper ’ s quit().

jeden wątek może mieć tylko jeden unikalny Looper i może mieć wiele unikalnych programów obsługi.

Tworzenie loopera i MessageQueue Dla wątku:

wątek otrzymuje Looper i MessageQueue, wywołując Looper.prepare() po jego uruchomieniu. Looper.prepare() identyfikuje wywołujący wątek, tworzy Looper i MessageQueue obiekt i kojarzy thread z nimi w klasie ThreadLocal storage. Looper.loop()musi zostać wywołane, aby uruchomić skojarzony looper. Podobnie, looper musi zostać zakończony jawnie przez looper.quit().

class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here // this will run in non-ui/background thread } }; Looper.loop(); } }

Tworzenie procedury obsługi wątku:

A Handler zostaje domyślnie skojarzony z wątkiem, który tworzy jego instancję poprzez wątek Looper, ale możemy wyraźnie powiązać go z wątkiem, przekazując wątek looper w konstruktorze Handler.

handler = new Handler() {@Overridepublic void handleMessage(Message msg) { // process incoming messages here // this will run in the thread, which instantiates it }};

wysyłanie wiadomości do MessageQueue przez Handler może odbywać się w dwóch trybach:

  1. Message: jest to klasa, która definiuje różne użyteczne metody do obsługi danych wiadomości. Aby wysłać obiekt ustawiamy zmienną obj.

Message msg = new Message();msg.obj = "Ali send message";handler.sendMessage(msg);

szczegółowy przegląd klasy Message można znaleźć tutaj: https://developer.android.com/reference/android/os/Message.html

Runnable: runnable można również umieścić w MessageQueue. Przykład: publikowanie i uruchamianie zadania w głównym wątku.

new Handler(Looper.getMainLooper()).post(new Runnable() {@Overridepublic void run() { // this will run in the main thread }});

w powyższym przykładzie tworzymy Handler i podajemy Looper powiązane z głównym wątkiem. To przyporządkowuje ten moduł obsługi do głównego wątku. Kiedy opublikujemy Runnable, zostanie on zaszeregowany w głównym wątku MessageQueue, a następnie wykonany w głównym wątku.

Handler jest w stanie manipulować wiadomościami na wiele różnych sposobów, które można znaleźć tutaj: https://developer.android.com/reference/android/os/Handler.html

Tworzenie własnego wątku i dostarczanie Lopper i MessageQueue nie jest właściwym sposobem radzenia sobie z problemem. Tak więc Android dostarczył HandlerThread (podklasa Thread), aby usprawnić proces. Wewnętrznie robi to samo, co my, ale w solidny sposób. Tak więc zawsze używaj HandlerThread.

jednym ze sposobów tworzenia HandlerThread jest podklasowanie go i przez większość czasu będziesz używał tej metody.

private class MyHandlerThread extends HandlerThread { Handler handler; public MyHandlerThread(String name) { super(name); } @Override protected void onLooperPrepared() { handler = new Handler(getLooper()) { @Override public void handleMessage(Message msg) { // process incoming messages here // this will run in non-ui/background thread } }; }}

Uwaga: utworzyliśmy instancję obsługi, gdy wywoływana jest funkcja onLooperPrepared(). Tak więc, że Handler może być powiązany z tym Looper.

  1. Looper jest przygotowywane dopiero po wywołaniu HandlerThread ’ S start(), tzn. po uruchomieniu wątku.
  2. A Handler można powiązać z HandlerThread, dopiero po przygotowaniu jej Looper.

inny sposób tworzenia HandlerThread:

HandlerThread handlerThread = new HandlerThread("MyHandlerThread");handlerThread.start();Handler handler = new Handler(handlerThread.getLooper());

Uwaga: HandlerThread musi wywołać myHandlerThread.quit(), aby uwolnić zasoby i zatrzymać wykonywanie wątku.

proponuję ćwiczyć powyższe kody, abyś mógł zrozumieć ich drobne szczegóły.

stworzyłem przykładowy projekt symulacji poczty. Poczta jest zbudowana na HandlerThread i klienci komunikować się z pomocą poczty. Klasa symulatora tworzy kilka botów klienckich i deleguje ich komunikację do MainActivity, która renderuje ją w kanale na żywo.

link do tego przykładu

stworzyłem również samouczek wideo na ten temat i Gorąco polecam go obejrzeć. Kliknij tutaj, aby obejrzeć teraz.

zostańmy również przyjaciółmi na Twitterze, Linkedin, Github i Facebooku.

Nauka to podróż, uczmy się razem!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.