0
0

【Android】launch(Dispatcher.Main.immediate) の呼び出し元も launch(Dispatcher.Main.immediate) だった場合、即時実行されない

Last updated at Posted at 2023-11-18

この記事では、簡単のため、CoroutineScope として GlobalScope を使用する。

要約

次のコードを実行すると、

GlobalScope.launch(Dispatchers.Main.immediate) {
    launch(Dispatchers.Main.immediate) {
        Log.i("", "in    launch")
    }
    Log.i("", "after launch")
}

期待に反して、launch ブロックが先に実行されず、次の順番でログが出力された。

after launch
in    launch

仕様かどうかは不明。

前提

メインスレッドにおいて、CoroutineDispatcher として Dispatchers.Main を使用して launch を行う次のようなコードを実行すると、

GlobalScope.launch(Dispatchers.Main) {
    Log.i("", "in    launch")
}
Log.i("", "after launch")

launch 呼び出しが処理を返してから launch のブロックが実行されるため、
ログは次のようになる。

after launch
in    launch

しかし Dispatchers.Main.immediate を使用した場合は、

GlobalScope.launch(Dispatchers.Main.immediate) {
    Log.i("", "in    launch")
}
Log.i("", "after launch")

launch のブロックが実行されてから launch 呼び出しが処理を返すため、
ログは次のようになる。

in    launch
after launch

Dispatchers.Main.immediate は、メインスレッドで処理を実行するが、既にメインスレッドにいる場合はディスパッチを行わないためである。

参考:

同様に、次のコードも launch(Dispatchers.Main.immediate) のブロックが先に実行される。

GlobalScope.launch(Dispatchers.Main) {
    launch(Dispatchers.Main.immediate) {
        Log.i("", "in    launch")
    }
    Log.i("", "after launch")
}
in    launch
after launch

本題

次のコードを実行すると、

GlobalScope.launch(Dispatchers.Main.immediate) {
    launch(Dispatchers.Main.immediate) {
        Log.i("", "in    launch")
    }
    Log.i("", "after launch")
}

期待に反して次の順番でログが出力された。

after launch
in    launch

次のように、外側の launchDispatchers.Main にして、内側の launchwithContext(Dispatchers.Main.immediate) で囲んだ場合は

GlobalScope.launch(Dispatchers.Main) {
    withContext(Dispatchers.Main.immediate) {
        launch(Dispatchers.Main.immediate) {
            Log.i("", "in    launch")
        }
        Log.i("", "after launch")
    }
}

期待どおり launch ブロックが先に実行された。

in    launch
after launch

どうやら launch(Dispatchers.Main.immediate) の呼び出し元の直近の launchlaunch(Dispatchers.Main.immediate) だと、launch のブロックが先に実行されないようだ。

次のように関数に切り出しても同じ。

@MainThread
fun launchWithImmediate() {
    GlobalScope.launch(Dispatchers.Main.immediate) {
        Log.i(tag, "in    launch")
    }
    Log.i(tag, "after launch")
}
GlobalScope.launch(Dispatchers.Main.immediate) {
    launchWithImmediate()
}
after launch
in    launch

そのため、このような関数は呼び出し元によっては期待通りの動作をしない可能性がある。


仕様かどうかは不明(バグのように思える)。
なにかご存じの方がいらっしゃれば、ご教示いただけると幸いです。

/以上

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0