AndroidでKotlin Coroutinesの使い所
まえがき
Coroutinesは、Coroutines are experimental in Kotlin 1.1で、実験的な機能ですのでプロダクトにはまだ早いですが、とても便利なので今のうちから備えておきたいところです。
Coroutines - Kotlin Programming Language
kotlinx.coroutines/coroutines-guide.mdを写経しながら勉強しています。
サンプルアプリを作りながら、ある程度パターンが見えてきました。
コルーチン
Coroutines(コルーチン)は中断・再開可能な関数です。
こちらに丁寧な説明があります。導入の仕方もあります。
Kotlin+Androidでasync/await - Qiita
Androidで使用する場合の良さそうなパターンがありましたので、ご紹介します。
MVPでもMVVMでも、使用できるパターンです。
概要
MVPでもMVVMでも大まかにいってこんな処理をすると思います。
UIから(タップなど)イベント -> 重たい処理(Webのapiを叩くなど) -> 結果をUIに反映
コルーチンがない現状は、RxJavaなどを用いて処理していると思います。
RxJavaの例
fun click(){ view.showLoading() repository.api() .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ view.hideLoading() view.showSuccess(it) }, { view.showError(it) }) }
パターン
重たい処理(Webのapiを叩くなど)の部分は処理している間は中断して他の処理をしてもらうようにsuspendをつけます。
class Repository{ suspend fun api() : Response{ //重たい処理 return Response() } }
UIから(タップなど)イベントと結果をUIに反映の部分はUIスレッドで行えるようにします。
launch(UI)をつかってUIスレッドで処理するように指定します。
fun click() = launch(UI) { }
そして、RepositoryのapiをUIスレッド以外で処理するようにCommonPoolを使用します。CommonPoolは新しいスレッドだと思ってください。apiはResponseを返すのでlaunchではなく asyncをつかって処理結果を受け取れるします。await()を使って、処理が終わるまで待ちます。その間は、中断され、処理が終わった時に再開されます。(非同期っぽく処理できる)
fun click() = launch(UI){ val response = async(CommonPool){ repository.api() }.await() }
その他付属品をつけてあげて
fun click() = launch(UI) { view.showLoading() val repository = Repository() try { val response = async(CommonPool) { repository.api() }.await() view.hideLoading() view.showSuccess(response) }catch (e: Throwable){ view.view.showError(e) } }
これで完成!
っと言いたいところだけど、このままだと、activityでonStopでJobをキャンセルしたいときにCommonPoolの処理がキャンセルされません。キャンセルできるように修正する必要があります。
こちらに丁寧な説明があります。AndroidでKotlin Coroutineを非同期で使うときの注意 – STAR_ZERO – Medium
fun click() = launch(UI) { view.showLoading() val repository = Repository() try { val response = async(context + CommonPool) { repository.api() }.await() view.hideLoading() view.showSuccess(response) }catch (e: Throwable){ view.view.showError(e) } }
まとめ
ソースコードが同期的な書き方でとても読みやすいです。非同期で依存関係がある場合コールバック地獄になりがちでしたが、とても簡潔に記述できるようになります。次回コルーチンでのUnitTestについて書きたいと思います。