package kotlinx.coroutines
...
public actual object Dispatchers {
...
@JvmStatic
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
...
}
Dispatchers.Main.immediate は MainCoroutineDispatcher に定義されており、Dispatchers.Main.immediate 自体も MainCoroutineDispatcher です。
package kotlinx.coroutines
...
public abstract class MainCoroutineDispatcher : CoroutineDispatcher() {
...
public abstract val immediate: MainCoroutineDispatcher
...
}
kotlinx-coroutines-android では HandlerDispatcher が MainCoroutineDispatcher を継承し、
package kotlinx.coroutines.android
...
public sealed class HandlerDispatcher : MainCoroutineDispatcher(), Delay {
...
public abstract override val immediate: HandlerDispatcher
}
HandlerContext が HandlerDispatcher を継承しています。
package kotlinx.coroutines.android
...
internal class HandlerContext private constructor(
private val handler: Handler,
private val name: String?,
private val invokeImmediately: Boolean
) : HandlerDispatcher(), Delay {
...
@Volatile
private var _immediate: HandlerContext? = if (invokeImmediately) this else null
override val immediate: HandlerContext = _immediate ?:
HandlerContext(handler, name, true).also { _immediate = it }
override fun isDispatchNeeded(context: CoroutineContext): Boolean {
return !invokeImmediately || Looper.myLooper() != handler.looper
}
...
}
HandlerContext では immediate にセットされる HandlerContext は invokeImmediately プロパティが true になる、ということがわかります。
invokeImmediately は isDispatchNeeded() で使われます。isDispatchNeeded() の実装をみると、invokeImmediately が false のときは常に isDispatchNeeded() が true を返すことがわかります。また Looper.myLooper() != handler.looper のときも isDispatchNeeded() が true を返すことがわかります。つまり、invokeImmediately が true かつ Looper.myLooper() == handler.looper のときだけ isDispatchNeeded() は false を返します。
このことから、immediate にセットされる HandlerContext では、Looper.myLooper() が handler.looper と同じだと isDispatchNeeded() が false を返すということがわかります。
isDispatchNeeded() は coroutine を dispatch メソッドで実行するべきかどうか判定するときに呼ばれます。デフォルトは true を返すようになっています。
package kotlinx.coroutines
...
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
...
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
...
}
つまり、(kotlinx-coroutines-android の) Dispatchers.Main.immediate はすでに UI スレッドにいる場合(現在の Looper.myLooper() が handler.looper と同じ場合)そのまますぐに実行される Dispatcher ということです。
例えば Dispatchers.Main を使った以下のコードだと
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CoroutineScope(Dispatchers.Main).launch {
println("1 : ${Thread.currentThread().name}")
}
println("2 : ${Thread.currentThread().name}")
}
}
出力は 2 が 1 より先になります。
I/System.out: 2 : main
I/System.out: 1 : main
CoroutineScope の dispatcher を Dispatchers.Main.immediate に変えると
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CoroutineScope(Dispatchers.Main.immediate).launch {
println("1 : ${Thread.currentThread().name}")
}
println("2 : ${Thread.currentThread().name}")
}
}
1 が先に出力されるようになります。
I/System.out: 1 : main
I/System.out: 2 : main
UI スレッドにいない場合はすぐには実行されず dispatch されます。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
CoroutineScope(Dispatchers.Default).launch {
println("3 : ${Thread.currentThread().name}")
CoroutineScope(Dispatchers.Main.immediate).launch {
println("1 : ${Thread.currentThread().name}")
}
println("4 : ${Thread.currentThread().name}")
}
println("2 : ${Thread.currentThread().name}")
}
}
I/System.out: 2 : main
I/System.out: 3 : DefaultDispatcher-worker-2
I/System.out: 4 : DefaultDispatcher-worker-2
I/System.out: 1 : main
viewModelScope, lifecycleScope は dispatcher として Dispatchers.Main.immediate が指定されています。
val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
get() = lifecycle.coroutineScope
val Lifecycle.coroutineScope: LifecycleCoroutineScope
get() {
while (true) {
...
val newScope = LifecycleCoroutineScopeImpl(
this,
SupervisorJob() + Dispatchers.Main.immediate
)
...
}
}
val ViewModel.viewModelScope: CoroutineScope
get() {
...
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
}