LoginSignup
13
11

More than 3 years have passed since last update.

LiveDataでCoroutineを使ってみた

Last updated at Posted at 2019-07-12

はじめに

非同期処理をCoroutineで行って、LiveDataで変更の通知を受け取る。
ということはよくあることかと思います。

この記事では、CoroutineLiveDataの処理を別々に行っていたものを
ひとまとめにして行う liveData{} の実用例を書きたいと思います。

便宜上、
CoroutineLiveDataを別々で処理する方法を Coroutine + LiveData
CoroutineLiveDataをまとめて処理する方法を LiveDataScope
と定義します。
(公式では単にliveDataですが、わかりにくいのでLiveDataScopeにさせてください)

LiveDataScopeを使用する場合、gradleに以下の設定が必要になります。

build.gradle
dependencies {
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha01" // or higher
}

それでは早速、見ていきましょう。

仕様

今回のサンプルの仕様は以下の通りです。

  • メイン画面(MainActivity)とログイン画面(LoginActivity)があります。
  • SharedPreferencesにログイン情報(loginId, password)が保存されている。
  • アプリを起動させたらMainActivityが作成されます。
  • MainActivityonCreate()SharedPreferencesに保存されているログイン情報をもとにログインAPIをコールし、照合すればメイン画面のまま居残り、照合しなければログイン画面に遷移する。

今回のサンプルで登場するクラスは以下の2つになります。

  • MainActivity
  • MainViewModel

また、Coroutine + LiveDataでの実装とLiveDataScopeでの実装を区別するため、
前者にはBefore、後者にはAfterの接頭詞をそれぞれのクラスに付与します。

Coroutine + LiveData での実装

BeforeMainActivity.kt
class BeforeMainActivity : AppCompatActivity() {

    lateinit var vm: BeforeMainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_before_main)

        vm = ViewModelProviders.of(this).get(BeforeMainViewModel::class.java)
        vm.login()
        // LiveDataの変更通知を受け取る
        vm.loginStatus.observe(this, Observer { isLogin ->
            if (!isLogin) {
                startActivity(Intent(this, LoginActivity::class.java))
            }
        })
    }
}
BeforeMainViewModel.kt
class BeforeMainViewModel(app: Application) : AndroidViewModel(app), CoroutineScope {
    // useCaseをDIしたり、userIdとpasswordはSharedPreferencesから取得していますが、比較しやすいよう割愛しています。
    ...
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Default + Job()

    // LiveData
    private val _loginStatus = MutableLiveData<Boolean>()
    val loginStatus: LiveData<Boolean> = _loginStatus

    // Coroutine
    fun login() {
        launch {
            try {
                // ログインAPIを叩き、照合結果を取得する
                val loginInfo = useCase.login(userId, password) // login()はsuspend関数
                _loginStatus.postValue(loginInfo.isLogin)
            } catch (e: Exception) {
                Timber.d(e)
            }
        }
    }
}

LiveDataScope での実装

AfterMainActivity.kt
class AfterMainActivity : AppCompatActivity() {

    lateinit var vm: AfterMainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_after_main)

        vm = ViewModelProviders.of(this).get(AfterMainViewModel::class.java)
        vm.loginStatus.observe(this, Observer { isLogin ->
            if (!isLogin) {
                startActivity(Intent(this, LoginActivity::class.java))
            }
        })
    }
}
AfterMainViewModel.kt
class AfterMainViewModel(app: Application) : AndroidViewModel(app) {

    // LiveDataScope
    val loginStatus = liveData {
        try {
            val loginInfo = useCase.login(userId, password)
            emit(loginInfo.isLogin)
        } catch (e: Exception) {
            Timber.d(e)
        }
    }
}

変わった点

  • ActivityからViewModellogin()を呼び出さなくて良くなった!
  • VeiwModelCoroutineScopeを実装する必要がなくなった!(そもそもviewModelScope使えば必要ないが…)
  • 今までイチイチViewModelで設定していたLiveDataプロパティの記述がなくなった!
  • LiveDataへの変更通知がpostValue()でなくemit()で行える!
  • AfterMainViewModelがイイ感じにスッキリ!

おわりに

コードの記述量が減るのはいいことですね。

参考

Use Kotlin coroutines with Architecture components
CoroutineLiveData

13
11
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
13
11