Kotlin coroutine入門  ③~ViewModelの中でcoroutineを扱ってみる~

[Kotlin coroutine入門 ②](https://qiita.com/ymkit/items/71dc7ddcbc6c24c26e83)の続きです。

前回の入門記事から、時間が経っていますが、今回は[ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ja)の中で、何らかのAPIと通信する体のcoroutineを実装しUIに反映して行きたいと思います！！

## 従来の構成 [MVVM](https://qiita.com/s_emoto/items/08e46b18f72c3b303b12) の[Rx](https://github.com/ReactiveX/RxJava)とcoroutine比較
#### Rx使用時
<img width="948" alt="スクリーンショット 2020-03-22 1.11.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123647/6a88a5ff-843d-1771-bc00-3be8d4acdcb0.png">

#### coroutine使用した場合
observableがまるっとcoroutinesに置き換わります。実際はReactiveStreamに似た、[Coroutine flow](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)でしょう。
<img width="890" alt="スクリーンショット 2020-03-22 1.12.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123647/ecfcf61c-7f9f-e9ad-c5b4-aa48163688ae.png">





## 実装
実際に実装をしてみます。(Repositry,ApiClientはApi.ktとして実装しています)

``` Api.kt
package ***

import kotlinx.coroutines.*

class Api(){
     suspend fun isHoge (): Deferred<Boolean> = coroutineScope {
            async (context = Dispatchers.IO) {
                true
            }
     }
}
```
``` MainActivityViewModel.kt
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 は子にキャンセルを伝播します。
キャンセルを忘れると、リークする可能性があります。

```MainActivityViewModel.kt

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause() {
        //jobに属している全てのtaskを停止する
        job.cancel()
    }

```

[こちら](https://qiita.com/kawmra/items/d024f9ab32ffe0604d39#%E3%82%AD%E3%83%A3%E3%83%B3%E3%82%BB%E3%83%AB%E3%81%AE%E4%BC%9D%E6%92%AD)が参考になりました。


## ViewModel KTX
ViewModel KTXの viewModelScope() により、Coroutine Scopeやキャンセルが不要となりました。
実際のコードは以下の通りです。

```(app)build.gradle
 dependencies {
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
    }
    
```

``` MainActivityViewModel.kt
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について
記載します。