LiveDataでCoroutineを使ってみた


はじめに

非同期処理を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