16
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

さまざまなCoroutineDispatcherの指定方法メモ

Last updated at Posted at 2019-03-02

概要

Kotlin CoroutineはDispatcherによって実行スレッドを制御する。そのDispatcherを切り替えることで、コルーチンのコードをテスト可能にできます。

DroidKaigiではCoroutinePluginによるやり方を作成して利用していました。そこにAppCoroutineDispatcherを使うPRが来ました。

3つの指定方法があるのでそれぞれ見ていきましょう。
CoroutinePlugin
AppCoroutineDispatcher
kotlinx-coroutines-test

CoroutinePluginによるやり方

CoroutinePluginはKotlin Corotuinesに存在するクラスではなく、アプリ内で作って使う形になります。
DroidKaigiアプリで採用されている方法
RxJavaのスケジューラーを真似たやり方でstaticでもって置いて切り替えができるようにする方式です。

object CoroutinePlugin {

    private val defaultIoDispatcher: CoroutineContext = Dispatchers.IO
    val ioDispatcher: CoroutineContext
        get() = ioDispatcherHandler?.invoke(
            defaultIoDispatcher
        ) ?: defaultIoDispatcher
    @set:VisibleForTesting
    var ioDispatcherHandler: ((CoroutineContext) -> CoroutineContext)? = null

...

テストでは CoroutinePlugin.mainDispatcherHandler = { Dispatchers.Default } などで置き換えが出来ます。

AppCoroutineDispatchersによるやり方

AppCoroutineDispatchersはこういうクラスを作って、これをInjectすることで置き換えるパターン。Chris Banesがやっていたものになります

data class AppCoroutineDispatchers(
    val database: CoroutineDispatcher,
    val disk: CoroutineDispatcher,
    val network: CoroutineDispatcher,
    val main: CoroutineDispatcher
)
@Singleton
@Provides
fun provideDispatchers(schedulers: AppRxSchedulers) = 
    AppCoroutineDispatchers(
        database = schedulers.database.asCoroutineDispatcher(),
        disk = schedulers.disk.asCoroutineDispatcher(),
        network = schedulers.network.asCoroutineDispatcher(),
        main = UI
    )

kotlinx-coroutines-testによるやり方

kotlinx-coroutines-testを依存関係に追加して、これだけです。

https://github.com/Kotlin/kotlinx.coroutines/tree/master/core/kotlinx-coroutines-test#using-in-your-project より

class SomeTest {
    
    private val mainThreadSurrogate = newSingleThreadContext("UI thread")

    @Before
    fun setUp() {
        Dispatchers.setMain(mainThreadSurrogate)
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
        mainThreadSurrogate.close()
    }
    
    @Test
    fun testSomeUI() = runBlocking {
        launch(Dispatchers.Main) {  // Will be launched in the mainThreadSurrogate dispatcher
            ...
        }
    }
}

これには一つだけ問題点があって、Dispatchers.Mainしか置き換えが出来ません。

どうしたか

CoroutinePluginによるやり方を使いました。

メリデメに関しては以下にまとまっています
https://github.com/DroidKaigi/conference-app-2019/issues/236

まずkotlinx-coroutines-testはMainしか置き換えできないので、用途に合わない場合も出てきそうでした。ただ今考えてみるとこれとrunBlocking()でテストを対応できた説もあるので、チャレンジしてみるのはいいと思います。
AppCoroutineDispatchersによるやり方はDroidKaigiではReceiveChannel.toLiveData()にわたす必要があるなど、大量に使われる部分でもDispatcherを渡していく必要が出てきたりなどするため、結構煩雑になってしまいそうでした。そのためCoroutinePluginを使う方法をそのまま使いました。
好みの問題もあるので、基本的にはkotlinx-coroutines-testでやってみて、何か問題があれば変えていくのがいいかなと思います。

16
8
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
16
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?