LoginSignup
4
1

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-03-22

Kotlin coroutine入門 ②の続きです。

前回の入門記事から、時間が経っていますが、今回はViewModelの中で、何らかのAPIと通信する体のcoroutineを実装しUIに反映して行きたいと思います!!

従来の構成 MVVMRxとcoroutine比較

Rx使用時

スクリーンショット 2020-03-22 1.11.50.png

coroutine使用した場合

observableがまるっとcoroutinesに置き換わります。実際はReactiveStreamに似た、Coroutine flowでしょう。
スクリーンショット 2020-03-22 1.12.02.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()
    }

こちらが参考になりました。

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

4
1
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
4
1