Możesz lepiej zrozumieć, czym jest Looper w kontekście GUI. Looper ma zrobić 2 rzeczy.
1) Looper przekształca normalny wątek , który kończy się po powrocie metody run (), w coś działającego nieprzerwanie do momentu uruchomienia aplikacji na Androida , co jest potrzebne w środowisku GUI (technicznie rzecz biorąc, nadal kończy się, gdy metoda run () powróci. Ale pozwól mi wyjaśnij, co mam na myśli poniżej).
2) Looper zapewnia kolejkę, w której zadania do wykonania są kolejkowane, co jest również potrzebne w środowisku GUI.
Jak zapewne wiesz, po uruchomieniu aplikacji system tworzy dla niej wątek wykonawczy, zwany „głównym”, a aplikacje na Androida zwykle domyślnie działają w całości na jednym wątku, domyślnie „głównym wątku”. Ale główny wątek nie jest jakimś tajnym, specjalnym wątkiem . To zwykły wątek podobny do wątków, które tworzysznew Thread()
kodu, co oznacza, że kończy się po powrocie metody run ()! Pomyśl o poniższym przykładzie.
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
Teraz zastosujmy tę prostą zasadę do aplikacji na Androida. Co by się stało, gdyby aplikacja na Androida działała na normalnym wątku? Wątek o nazwie „main” lub „UI” lub cokolwiek uruchamia twoją aplikację i rysuje cały interfejs użytkownika. Zatem pierwszy ekran jest wyświetlany użytkownikom. Co teraz? Główny wątek kończy się? 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 śpi. Budzi się, co oznacza przerwanie, gdy pobierane jest nowe zadanie. Jak dotąd tak dobrze, ale w tej chwili potrzebujemy struktury danych przypominającej kolejkę do obsługi wielu zadań. Pomyśl o przypadku, gdy użytkownik dotknie ekranu szeregowo, a zakończenie zadania zajmuje więcej czasu. Musimy więc mieć strukturę danych, aby przechowywać zadania do wykonania według kolejności zgłoszeń. Można również sobie wyobrazić, że implementacja wątku działającego i przetwarzającego, gdy dotrze do niego przy użyciu przerwania, nie jest łatwa i prowadzi do złożonego i często niemożliwego do utrzymania kodu. W tym celu wolelibyśmy stworzyć nowy mechanizm i na tym właśnie polega Looper . Oficjalny dokument klasy Loopermówi: „Wątki domyślnie nie mają z nimi powiązanej pętli komunikatów”, a Looper jest klasą „używaną do uruchamiania pętli komunikatów dla wątku”. Teraz możesz zrozumieć, co to znaczy.
Aby to wyjaśnić, sprawdźmy kod, w którym główny wątek jest transformowany. Wszystko to dzieje się w klasie ActivityThread . W metodzie main () możesz znaleźć poniższy kod, który zamienia normalny główny wątek w coś, czego potrzebujemy.
public final class ActivityThread {
...
public static void main(String[] args) {
...
Looper.prepareMainLooper();
Looper.loop();
...
}
}
i Looper.loop()
metoda zapętla się w nieskończoność i usuwa z kolejki komunikat i przetwarza pojedynczo:
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
msg.target.dispatchMessage(msg);
...
}
}
Zasadniczo Looper to klasa stworzona w celu rozwiązania problemu występującego w środowisku GUI. Ale tego rodzaju potrzeby mogą się również zdarzyć w innej sytuacji. W rzeczywistości jest to dość znany wzorzec dla aplikacji wielowątkowych i możesz dowiedzieć się więcej na ten temat w „ Concurrent Programming in Java ” Douga Lei (zwłaszcza rozdział 4.1.4 „Wątki robocze” byłby pomocny). Można również sobie wyobrazić, że ten rodzaj mechanizmu nie jest unikalny w systemie Android, ale wszystkie ramy GUI mogą wymagać nieco podobnego do tego. Prawie taki sam mechanizm można znaleźć w frameworku Java Swing.