tgoop.com/dev_easy_notes/21
Last Update:
{3/4} В этом посте используется код на Kotlin, просьба Java дИдов не пугаться!
Понемногу у нас с вами складывается картина того, каким образом работает UI в android. Мы разобрали что такое Looper, что такое Message и MessageQueue. Узнали, что в Looper посылаются задачи через MessageQueue. Возникает вопрос❓, как именно послать задачу в Looper, так как прямого доступа к MessageQueue у нас нет. Тут на сцену выходит Handler.
Прежде чем начнем разбирать Handler введем 2️⃣ понятия:
☝️поток consumer - поток, который ждет сообщения, тот, который вызывал Looper.loop().
✌️поток producer - поток, который создает сообщения и посылает их потоку consumer через Handler.
Поток consumer и поток producer могут быть одним потоком, как это может быть разберем далее.
Каждый Android разработчик хотя бы раз в своих проектах использовал Handler. Как уже сказано выше, нужен он для того, чтобы посылать задачи из потока producer в поток consumer.
У Handler есть несколько конструкторов, интересные пожалуй только 2️⃣ это:
☝️дефолтый коструктор без аргументов Handler()
✌️конструктор с аргументом типа Looper Handler(looper: Looper)
Когда используют конструктор без Looper, Handler пытается найти его через Looper.myLooper() и если его не находит, то падает❌.
Допустим в Activity.onCreate вы вызовете Handler().post{ doSmth() }
- тут создается Handler, который через метод Looper.myLooper() получает Looper который привязан к Main Thread🧶, что аналогично записи Handler(Looper.getMainLooper()).post{ doSmth() }
. Но если попытаемся тоже самое сделать на потоке без Looper, конструктор упадет💣:
❌
Thread {
val handler = Handler() // - тут мы упадем, так как у этого потока нет Looper
🔁, подробнее смотри в посте про Looper
handler.post { doSmth() }
}.start()
Конструктор с входным параметром в виде Looper предпочтительнее, так как тогда вы явно задаете Looper🔁, в который будут post’иться сообщения. С ним все довольно очевидно, на вход нужно подать Looper в который будут посылаться задачи.
Теперь посмотрим на слудующий кусок кода:
fun onCreate() { // этот метод вызывает система
Handler().post {
Log.d("hello from handler thread ${Thread.currentThread().name}")
}
Log.d("hello from onCreate thread ${Thread.currentThread().name}")
}
Для начала попробуйте ответить сами, что будет выведено в лог❓ В логах мы увидим следующую последовательность:
- "hello from onCreate thread main"
- "hello from handler thread main"
Когда вызываем метод post, переданный Runnable оборачивается в Message и через MessageQueue подается на Looper consumer потока. Однако система все методы жизненного цикла активности тоже вызывает через Looper потока Main🧶, даже сам метод onCreate() вызывался примерно так:
val activity = getCurrentActivity()
val handler = Handler()
handler.post {
activity.onCreate()
}
Следовательно, когда мы вызываем Handler().post{ Log.d("hello from handler thread ${Thread.currentThread().name}") }
эта задача кладется в очередь и выполнится после того, как завершится метод onCreate().
Этот пример очень сильное упрощение того, что происходит на самом деле, но суть та же. Это тот случай, когда поток consumer и поток producer это один и тот же поток. Именно этим фактом, обуславливается асинхронность в UI. Когда мы создаем транзакцию для показа фрагмента, после метода commit эта транзакция также кладется в MessageQueue через Handler, и выполняется позже Looper’ом Main Thread🧶, также происходит и с показом новой🆕 Activity и многими другими вещами вроде анимаций и т.п.
Стоит упомянуть еще одну интересную особенность, так уж вышло, что огромное количество багов связаных с View на Android можно решить просто отложив задачу Handler().postDelayed(100) { //doSmth }
.
Помните мы разбирали, что у Message есть специальное поле when, так вот, когда вызываем post у Handler, там используется SystemClock.uptimeMillis()), наподобие System.getCurrentTimeMillis(), а когда вызываем postDelayed, то входной аргумент delay прибавляется к SystemClock.uptimeMillis()) и записывается в поле when, а дальше магия сортировки, которая обсуждалась туть.
BY Dev Easy Notes

Share with your friend now:
tgoop.com/dev_easy_notes/21