Kotlin coroutine入門 ②の続きです。
前回の入門記事から、時間が経っていますが、今回はViewModelの中で、何らかのAPIと通信する体のcoroutineを実装しUIに反映して行きたいと思います!!
従来の構成 MVVM のRxとcoroutine比較
Rx使用時
coroutine使用した場合
observableがまるっとcoroutinesに置き換わります。実際はReactiveStreamに似た、Coroutine flowでしょう。
実装
実際に実装をしてみます。(Repositry,ApiClientはApi.ktとして実装しています)
package ***
import kotlinx.coroutines.*
class Api(){
suspend fun isHoge (): Deferred<Boolean> = coroutineScope {
async (context = Dispatchers.IO) {
true
}
}
}
package ***
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
class MainActivityViewModel() : ViewModel(), CoroutineScope, LifecycleObserver {
private val job = Job()
// Dispatcher.Main を指定しているためこのスコープで起動するコルーチンはメインスレッドで動作する。
// Job として上で定義した job を渡しているので、すべてのコルーチンはこの job の子になる。
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
val scope = CoroutineScope(coroutineContext)
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
val api = Api()
//main threadで実行する
scope.launch {
try {
api.isHoge().await().let {
Log.d("MainActivityViewModel", it.toString())
}
}catch (e: Throwable){
Log.d("MainActivityViewModel", e.message)
}
}
Log.d("MainActivityViewModel","onResumeEnd")
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
//jobに属している全てのtaskを停止する
job.cancel()
}
}
Jobキャンセルについて
親ジョブがキャンセルされると子Job は子にキャンセルを伝播します。
キャンセルを忘れると、リークする可能性があります。
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
//jobに属している全てのtaskを停止する
job.cancel()
}
こちらが参考になりました。
ViewModel KTX
ViewModel KTXの viewModelScope() により、Coroutine Scopeやキャンセルが不要となりました。
実際のコードは以下の通りです。
dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
}
package ****
import android.util.Log
import androidx.lifecycle.*
import kotlinx.coroutines.launch
class MainActivityViewModel() : ViewModel(), LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
val api = Api()
//main threadで実行する
Log.d("MainActivityViewModel","onResumeStart")
viewModelScope.launch {
try {
api.isHoge().await().let {
Log.d("MainActivityViewModel", it.toString())
}
}catch (e: Throwable){
Log.d("MainActivityViewModel", e.message)
}
}
Log.d("MainActivityViewModel","onResumeEnd")
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
//job cancel は不要
}
}
コードもすっきりしましたし、cancelが漏れることもないでしょう。
まとめ
viewModelScopeが登場しキャンセル漏れも防げるようになりましたね。
この辺りはRxの時もDispose忘れなどあったので、すごく助かります。
次回はRxのReactiveStreamでおなじみである、coroutine channel flowについて
記載します。