TGViewer
Android under the hood Android under the hood @android_under_the_hood · 1.53K subscribers
Post #52 2.03K
Dispatchers.Main под капотом.

CoroutineDispatcher - один из базовых компонентов Kotlin корутин, предназначенный для переключения потоков, чтобы разобраться как он работает под капотом, немного погрузимся в общий принцип работы корутин.

Любая корутина создаёт Continuation объект и передаёт его suspend функциям, те в свою очередь вызывают метод continuation.resumeWith() когда нужно вернуть результат или ошибку, это можно сравнить с обычными callback'ами, только с тем отличием, что в Kotlin Coroutines всё реализовано под капотом + есть дополнительные механизмы в стиле Structured Concurrency.

Но чтобы переключать потоки знания общего принципа достаточно, давайте задумаемся на минутку, что надо сделать, чтобы результат callback'а или Continuation'а в нашем случае был на другом потоке?

Да конечно вызвать Continuation.resumeWith() на другом потоке:

// IO thread
val ctx = coroutineContext
// метод isDispatchNeeded проверяет есть ли смысл переключать корутину на главный поток, чтобы не делать лишних переключений, если корутина и так находится на главном потоке
if (mainDispatcher.isDispatchNeeded(ctx)) {
// метод dispatch выполняет блок кода на главном потоке
mainDispatcher.dispatch(ctx) {
// Main thread
continuation.resumeWith(...)
}
} else {
// уже находимся на главном потоке, продолжаем выполнение
continuation.resumeWith(...)
}


Если у вас появится желание написать свой CoroutineDispatcher, то вы должны обязательно реализовать метод dispatch() и было бы неплохо переопределить isDispatchNeeded() чтобы исключить лишние переключения.

Вернёмся к главному потоку, в Android такой Dispatcher или как он назван в исходниках HandlerContext реализован с помощью Handler'а:

// реализация для Dispatchers.Main
override fun isDispatchNeeded(...) = true

// реализация для Dispatchers.Main.immediate
override fun isDispatchNeeded(...): Boolean {
// сравнивает Looper текущего потока с главным
// handler.looper это Looper.getMainLooper()
return Looper.myLooper() != handler.looper
}

override fun dispatch(
context: CoroutineContext,
block: Runnable
) {
// handler.post() выполняет код на главном потоке
if (!handler.post(block)) { ... }
}


Dispatchers.Main не делает проверок на главный поток, поэтому если написать несколько вложенных viewModelScope.launch() вызовов всегда будет происходить переключение текущего потока на главный через Handler.post(), даже если код уже выполняется на главном потоке.

Чтобы избежать ненужных переключений используйте Dispatchers.Main.immediate, в таком случае если код уже выполняется на главном потоке, метод isDispatchNeeded() вернёт false и dispatch() не будет вызван.

P.S. Хочу порекомендовать крутой канал с авторским контентом и полезными материалами в области Android разработки @dolgo_polo_dev

Пишите в комментах ваше мнение и всем хорошего кода!

Telegraph версия
  • 🔥 17
  • 👍 5
  • 🥰 1
More from @android_under_the_hood
  1. Oct 4, 2026Всем привет, хочу поделиться проектом, который запустил 1 октября — ЗдесьЯ. Это цифровая с…
  2. Oct 2, 2026Kotlin lambdas, часть I. До версии Kotlin 2.0 лямбды компилировались в анонимные классы, р…
  3. Sep 27, 2026Делегат свойства в Kotlin. В Kotlin есть конструкция, которой нет в JVM: var name by NameD…
  4. Sep 23, 2026val vs var под капотом. На уровне Kotlin все просто: val x = 10 var y = 20 y = 30 // компи…
  5. Sep 20, 2026Возвращение Прошло уже полгода с последней записи на канале. За это время в моей жизни про…
  6. Feb 5, 2026Пару фактов о Go, часть II. 4) Вместо Kotlin Nullability указатели как в С/C++, то есть об…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →