最近始めたばかりのプロジェクトで、コルーチンをいい感じに使えるようになってきたので知見を共有しようと思います。
バックグラウンドで処理からのViewに反映
MVVMアーキテクチャを用いたプロジェクトを想定してます。
リポジトリの準備。今回はQiitaのapiを叩いてみます。
// Response
data class SampleItem(
@Json(name = "id")
val id: String,
@Json(name = "title")
val title: String
)
// Request
@GET("items")
fun items(
@Query("page") page: Int,
@Query("par_page") perPage: Int
): Deferred<List<SampleItem>>
// コルーチン内で呼びます
suspend fun getItemList(): List<SampleItem> {
return items(page = 1, perPage = 10).await()
}
バックグラウンドでのコルーチンの処理状態を、View側で検知するための状態を定義します。
sealed class Status<out T> {
object Loading : Status<Nothing>()
data class Success<T>(val data: T) : Status<T>()
data class Failure(val throwable: Throwable) : Status<Nothing>()
}
コルーチンをViewModel内で実行します。
class SampleViewModel() {
// 本当はインジェクションとか使う
private val sampleRepository = SampleRepository()
private val _status = MutableLiveData<Status<List<SampleItem>>>()
// View側の監視対象
// 既存値と同じ値が設定されても通知されないようにする
val status = _status.distinctUntilChanged()
init {
load()
}
private fun load() = viewModelScope.launch {
// 初期状態にLoadingを設定
_status.value = Status.Loading
// IOスレッドで処理を実行
runCatching { withContext(Dispatchers.IO) { sampleRepository.getItemList() } }
// 成功(完了)したら状態をSuccessに変更
.onSuccess { _status.value = Status.Success(it) }
// 失敗したら状態をFailureに変更
.onFailure { _status.value = Status.Failure(it) }
}
}
あとはViewModelのstatusをView側で監視してやれば、めでたく非同期処理が完了です。
class SampleActivity : AppCompatActivity() {
// 本当はDIとか使ってください
private val viewModel = SampleViewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.sample_activity)
// ViewModelのstatusを監視して、変化があった場合にonStateChangedを実行します
viewModel.status.observe(this, Observer { onStateChanged(it) })
}
private fun onStateChanged(it: Status<List<SampleItem>>) = when(it) {
is Status.Loading -> {
// 処理中の処理
}
is Status.Success -> {
// 成功(完了)後の処理
// it.dataで取得した値を参照したり
}
is Status.Failure -> {
// 失敗後の処理
// it.throwableでエラー処理とかできる
}
}
}
まとめ
だいぶフレンドリーな感じに仕上がってきました。今後もコルーチンは使い続けていくはずなので、この機会に覚えておくといいかもです。