Sprawdziłem oficjalny Android dokumentacji / instrukcji dla Looper
, Handler
i MessageQueue
. Ale nie mogłem tego zrozumieć. Jestem nowy w Androidzie i bardzo się pomyliłem z tymi koncepcjami.
źródło
Sprawdziłem oficjalny Android dokumentacji / instrukcji dla Looper
, Handler
i MessageQueue
. Ale nie mogłem tego zrozumieć. Jestem nowy w Androidzie i bardzo się pomyliłem z tymi koncepcjami.
A Looper
to pętla obsługi wiadomości: odczytuje i przetwarza elementy z pliku MessageQueue
. Looper
Klasa jest zwykle stosowany w połączeniu z HandlerThread
(podklasą Thread
).
A Handler
to klasa narzędziowa, która ułatwia interakcję z - Looper
głównie poprzez wysyłanie wiadomości i Runnable
obiektów do wątku MessageQueue
. Po utworzeniu Handler
jest on powiązany z określonym Looper
(i skojarzonym wątkiem i kolejką komunikatów).
W typowym użyciu tworzysz i uruchamiasz HandlerThread
, a następnie tworzysz Handler
obiekt (lub obiekty), za pomocą którego inne wątki mogą współdziałać z HandlerThread
instancją. Handler
Muszą być tworzone podczas pracy na HandlerThread
, chociaż raz stworzony nie ma ograniczeń, co można używać nici Handler
„s metod harmonogramowania ( post(Runnable)
itd)
Główny wątek (inaczej wątek interfejsu użytkownika) w aplikacji na Androida jest konfigurowany jako wątek obsługi przed utworzeniem instancji aplikacji.
Oprócz docs klasowych, jest ładny dyskusja o tym wszystkim tutaj .
PS Wszystkie wyżej wymienione zajęcia znajdują się w pakiecie android.os
.
MessageQueue
państwa, żeMessageQueue
jest „ klasy niskopoziomowe trzyma listę wiadomości mają być wysyłane przezLooper
. ”Powszechnie wiadomo, że aktualizowanie elementów interfejsu użytkownika bezpośrednio z wątków innych niż główny wątek w systemie Android jest nielegalne . Ten dokument dotyczący systemu Android ( Obsługa kosztownych operacji w wątku interfejsu użytkownika ) sugeruje kroki, które należy wykonać, jeśli musimy uruchomić oddzielny wątek, aby wykonać kosztowną pracę i zaktualizować interfejs użytkownika po zakończeniu. Chodzi o to, aby utworzyć obiekt Handler powiązany z głównym wątkiem i wysłać do niego Runnable w odpowiednim czasie. To
Runnable
będzie powoływać się na głównym wątku . Ten mechanizm jest zaimplementowany w klasach Looper i Handler .Looper
Klasa utrzymuje kolejka komunikatów , które zawiera listę wiadomości . Ważną cechą Loopera jest to, że jest powiązany z wątkiem, w którymLooper
jest tworzony . To skojarzenie jest utrzymywane na zawsze i nie można go zerwać ani zmienić. Pamiętaj również, że wątek nie może być powiązany z więcej niż jednymLooper
. Aby zagwarantować to powiązanie,Looper
jest przechowywany w pamięci lokalnej wątku i nie można go utworzyć bezpośrednio za pomocą jego konstruktora. Jedynym sposobem, aby go utworzyć jest zwrócenie przygotować metody statyczneLooper
. Przygotowanie metody najpierw bada ThreadLocalbieżącego wątku, aby upewnić się, że nie ma już powiązanego Looper z wątkiem. Po badaniu nowyLooper
jest tworzony i zapisywany wThreadLocal
. Po przygotowaniuLooper
możemy wywołać na niej metodę loop , aby sprawdzić, czy są nowe wiadomości iHandler
zająć się nimi.Jak sama nazwa wskazuje,
Handler
klasa jest głównie odpowiedzialna za obsługę (dodawanie, usuwanie, wysyłanie) komunikatów aktualnego wątkuMessageQueue
.Handler
Przykład jest również związany z gwintem. Wiązania pomiędzy Handler i nici uzyskuje się poprzezLooper
aMessageQueue
. AHandler
jest zawsze powiązany z aLooper
, a następnie powiązany z wątkiem skojarzonym zLooper
. W przeciwieństwie doLooper
wielu instancji programu obsługi można powiązać z tym samym wątkiem. Za każdym razem, gdy wywołujemy post lub inne podobne metodyHandler
, do skojarzonego z nim dodawana jest nowa wiadomośćMessageQueue
. Pole docelowe wiadomości jest ustawione na bieżącąHandler
instancję. KiedyLooper
odebrał tę wiadomość, wywołuje dispatchMessage w polu docelowym wiadomości, dzięki czemu wiadomość jest kierowana z powrotem do instancji Handler, która ma być obsłużona, ale we właściwym wątku. Relacje międzyLooper
,Handler
iMessageQueue
przedstawiono poniżej:źródło
Zacznijmy od Loopera. Możesz łatwiej zrozumieć związek między Looper, Handler i MessageQueue, gdy zrozumiesz, czym jest Looper. Możesz także lepiej zrozumieć, czym jest Looper w kontekście frameworka GUI. Looper jest stworzony do robienia 2 rzeczy.
1) Looper przekształca normalny wątek , który kończy się po
run()
powrocie metody, w coś, co działa nieprzerwanie do momentu uruchomienia aplikacji na Androida , co jest potrzebne w ramach GUI (technicznie rzecz biorąc, nadal kończy się, gdyrun()
metoda zwraca. Ale pozwól mi wyjaśnić, o co mi chodzi, poniżej).2) Looper zapewnia kolejkę, w której umieszczane są zadania do wykonania, co jest również wymagane w ramach GUI.
Jak być może wiesz, kiedy aplikacja jest uruchamiana, system tworzy dla niej wątek wykonywania, zwany „głównym”, a aplikacje na Androida zwykle działają w całości w pojedynczym wątku, domyślnie „głównym wątku”. Ale główny wątek nie jest jakimś tajnym, specjalnym wątkiem . To po prostu zwykły wątek, który możesz również utworzyć za pomocą
new Thread()
kodu, co oznacza, że kończy się, gdy jegorun()
metoda zwraca! Pomyśl o poniższym przykładzie.Teraz zastosujmy tę prostą zasadę do aplikacji na Androida. Co by się stało, gdyby aplikacja na Androida była uruchamiana w normalnym wątku? Wątek o nazwie „main” lub „UI” lub jakikolwiek inny uruchamia aplikację i rysuje cały interfejs użytkownika. Tak więc pierwszy ekran jest wyświetlany użytkownikom. Co teraz? Główny wątek się kończy? Nie, nie powinno. Powinien poczekać, aż użytkownicy coś zrobią, prawda? Ale jak możemy osiągnąć takie zachowanie? Cóż, możemy spróbować z
Object.wait()
lubThread.sleep()
. Na przykład główny wątek kończy swoje początkowe zadanie, aby wyświetlić pierwszy ekran i usypia. Budzi się, co oznacza, że jest przerywany, gdy pobierana jest nowa praca do wykonania. Jak na razie dobrze, ale w tej chwili potrzebujemy struktury danych podobnej do kolejki do przechowywania wielu zadań. Pomyśl o przypadku, gdy użytkownik kolejno dotyka ekranu, a wykonanie zadania zajmuje więcej czasu. Musimy więc mieć strukturę danych, aby przechowywać zadania do wykonania w trybie „pierwszy na wejściu, pierwszy na wyjściu”. Możesz również sobie wyobrazić, że implementowanie zawsze działającego i przetwarzającego zadania, gdy nadejdzie wątek przy użyciu przerwania, nie jest łatwe i prowadzi do złożonego i często niemożliwego do utrzymania kodu. Wolelibyśmy stworzyć nowy mechanizm do tego celu i na tym właśnie polega Looper . Oficjalny dokument klasy Loopermówi: „Wątki domyślnie nie mają skojarzonej pętli komunikatów”, a Looper to klasa „używana do uruchamiania pętli komunikatów dla wątku”. Teraz możesz zrozumieć, co to oznacza.Przejdźmy do Handler i MessageQueue. Po pierwsze MessageQueue to kolejka, o której wspomniałem powyżej. Znajduje się wewnątrz Loopera i to wszystko. Możesz to sprawdzić za pomocą kodu źródłowego klasy Looper . Klasa Looper ma zmienną składową MessageQueue.
Więc czym jest Handler? Jeśli jest kolejka, to powinna istnieć metoda, która pozwoli nam umieścić w kolejce nowe zadanie, prawda? To właśnie robi Handler. Możemy umieścić nowe zadanie w kolejce (MessageQueue) używając różnych
post(Runnable r)
metod. Otóż to. Chodzi o Looper, Handler i MessageQueue.Moje ostatnie słowo jest takie, że w zasadzie Looper to klasa stworzona, aby rozwiązać problem występujący w ramach GUI. Ale tego rodzaju potrzeby mogą się również zdarzyć w innych sytuacjach. W rzeczywistości jest to dość znany wzorzec dla aplikacji wielowątkowych i możesz dowiedzieć się o nim więcej w "Programowaniu współbieżnym w Javie" autorstwa Douga Lea (szczególnie pomocny byłby rozdział 4.1.4 "Wątki robocze"). Możesz także sobie wyobrazić, że ten rodzaj mechanizmu nie jest unikalny w ramach systemu Android, ale wszystkie frameworki GUI mogą wymagać czegoś podobnego do tego. Prawie ten sam mechanizm można znaleźć we frameworku Java Swing.
źródło
MessageQueue
: Jest to klasa niskiego poziomu przechowująca listę wiadomości do wysłania przezLooper
. Wiadomości nie są dodawane bezpośrednio do aMessageQueue
, ale raczej poprzezHandler
obiekty skojarzone zLooper
. [ 3 ]Looper
: Przechodzi przez pętlę,MessageQueue
która zawiera wiadomości do wysłania. Faktyczne zadanie zarządzania kolejką wykonuje podmiotHandler
odpowiedzialny za obsługę (dodawanie, usuwanie, wysyłanie) komunikatów w kolejce komunikatów. [ 2 ]Handler
: To pozwala na wysyłanie i procesMessage
iRunnable
przedmioty związane z wątkuMessageQueue
. Każda instancja Handler jest powiązana z pojedynczym wątkiem i kolejką komunikatów tego wątku. [ 4 ]Kiedy tworzysz nowy
Handler
, jest on powiązany z wątkiem / kolejką komunikatów wątku, który go tworzy - od tego momentu będzie dostarczał komunikaty i pliki do uruchomienia do tej kolejki komunikatów i wykonywał je, gdy wychodzą z kolejki komunikatów .Prosimy zapoznać się z poniższym obrazkiem [ 2 ], aby lepiej zrozumieć.
źródło
Rozszerzenie odpowiedzi, przez @K_Anas, o przykład, Jak stwierdzono
na przykład jeśli spróbujesz zaktualizować interfejs użytkownika za pomocą Thread.
Twoja aplikacja ulegnie awarii z wyjątkiem.
innymi słowy, musisz użyć,
Handler
który zachowuje odniesienie doMainLooper
ieMain Thread
lubUI Thread
i przekazać zadanie jakoRunnable
.źródło