Что такое Looper и как он работает
Что проверяют
Понимаете ли вы, что главный поток это не просто «поток, где живёт интерфейс», а бесконечный цикл разбора очереди сообщений. На этом держатся правила про обновление интерфейса из фона, про ANR и про то, почему обычный поток не умеет принимать задачи после запуска.
Как отвечать
Механизм это три связанных объекта. MessageQueue хранит сообщения, упорядоченные по времени срабатывания. Looper крутит цикл, достаёт очередное сообщение и отдаёт адресату. Handler кладёт сообщения в очередь и получает их обратно в своём колбэке. Looper привязан к потоку через ThreadLocal, ровно один на поток.
class Worker extends Thread {
public void run() {
Looper.prepare();
handler = new Handler(Looper.myLooper()) { /* handleMessage */ };
Looper.loop(); // не вернётся, пока очередь не остановят
}
}Главный поток получает свой Looper при старте процесса, поэтому обращение к главному Looper через Handler это штатный способ вернуться в интерфейс из фона. Руками prepare и loop почти не пишут: есть HandlerThread, который делает это сам и отдаёт готовый Looper.
Останавливают цикл методами экземпляра: quit выбрасывает очередь целиком, quitSafely доработает сообщения, чей срок уже наступил. Главный Looper останавливать нельзя.
Где спотыкаются
Создают Handler в обычном потоке и получают исключение о том, что prepare не вызывался. Та же природа у жалобы «поток не принимает задачи»: без цикла поток отработал метод run и завершился.
Считают, что Looper делает работу параллельной. Он обрабатывает сообщения строго по одному, и долгая операция внутри обработчика блокирует всю очередь; на главном потоке это прямая дорога к ANR.
Держат Handler нестатическим внутренним классом: он держит ссылку на экран, а отложенное сообщение держит сам Handler. Экран закрыт, а объект жив до срабатывания таймера; лечится снятием сообщений при уничтожении.