LoginSignup
2

More than 3 years have passed since last update.

Kotlinで非同期化を行う時はCoroutineを使おう

Last updated at Posted at 2019-10-16

概要

Androidでは、UIをブロッキングしないために非同期化を行うことは必須である。ネットワークやストレージへのアクセスに時間がかかった場合、UIは反応しなくなりAndroidはApplication not respondingでクラッシュする。

問題を解決する一番シンプルな方法はスレッドを直接起動することである。Kotlinならthread{}ブロックでスレッドが起動できる。 だが、実行スレッド数を意識する必要があり、リソースを大量に消費すると想定通りに動作しなくなる可能性がある。

AndroidのAPIではAsyncTaskとAsyncTaskLoaderがある。内部でThreadPoolを用いているため、直接スレッドを起動する問題はないが、Workerスレッドで計算した値をUIスレッドで受け取るという構造なので、シンプルさには欠けている。

ThreadPoolを直接使うのも同様である。理想はthreadのように簡単に使えてかつ軽いことで、この条件を満たすのがKotlinのCoroutineである。

実装

Coroutineはlifecycle.coroutineScope.launchを使うか、GlobalScope.launchを使用するで、非同期に実行することができる。以下のサンプルコードでは、ログに実行しているスレッドを出力している。

class MainActivity : AppCompatActivity() {

    @ObsoleteCoroutinesApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        lifecycle.coroutineScope.launch {
            launch {
                Log.d("Test", "thread name:${Thread.currentThread().name} in just launch")
            }
            launch(Dispatchers.Default) {
                Log.d("Test", "thread name:${Thread.currentThread().name} in default")
            }
            launch(newSingleThreadContext("MyOwnThread")) {
                Log.d("Test", "thread name:${Thread.currentThread().name} in own")
            }
        }

        GlobalScope.launch {
            Log.d("Test", "thread name:${Thread.currentThread().name} in global")
        }
    }
}

lifecycle.coroutineScope.launchを使用するため、build.graldeのdependenciesに以下を追加する。

implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-beta01'

以下実行結果である。

07-21 02:04:57.471 D/Test    ( 8979): thread name:main in just launch
07-21 02:04:57.472 D/Test    ( 8979): thread name:MyOwnThread in main
07-21 02:04:57.473 D/Test    ( 8979): thread name:DefaultDispatcher-worker-2 in default
07-21 02:04:57.478 D/Test    ( 8979): thread name:DefaultDispatcher-worker-2 in global

特別に理由がなければDispachers.Defaultを渡したlaunchを使用すべきだ。これは共有スレッドで実行を行う。共有スレッドが占有されたくない場合には、 newSingleThreadContext("MyOwnThread") のように自分でCoroutineContextのインスタンスを作成してlaunchに渡せば、新規スレッドで実行できる。

GlobalScope.launchを使用する方法は、調べるといろいろなサイトで例として乗っているが、アプリが終了するまでコルーチンが残り続けるようだ。実行されるスレッドはlifecycle.coroutineScope.launch内のlaunchにDispachers.Defaultを渡した場合と同じなので、lifecycle.coroutineScope.launchの方を使っておけば問題ない。

Coroutineは他にもいろいろな機能があるが、threadの代わりに使うだけでもメリットがある。

参考資料

launchに渡しているDispatcherとかCoroutineContextに関する話

https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html

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
2