はじめに
非同期処理をCoroutine
で行って、LiveData
で変更の通知を受け取る。
ということはよくあることかと思います。
この記事では、Coroutine
とLiveData
の処理を別々に行っていたものを
ひとまとめにして行う liveData{}
の実用例を書きたいと思います。
便宜上、
Coroutine
とLiveData
を別々で処理する方法を Coroutine + LiveData
Coroutine
とLiveData
をまとめて処理する方法を 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
が作成されます。 -
MainActivity
のonCreate()
で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
からViewModel
のlogin()
を呼び出さなくて良くなった! -
VeiwModel
でCoroutineScope
を実装する必要がなくなった!(そもそもviewModelScope
使えば必要ないが…) - 今までイチイチ
ViewModel
で設定していたLiveData
プロパティの記述がなくなった! -
LiveData
への変更通知がpostValue()
でなくemit()
で行える! -
AfterMainViewModel
がイイ感じにスッキリ!
おわりに
コードの記述量が減るのはいいことですね。
参考
Use Kotlin coroutines with Architecture components
CoroutineLiveData