この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Lifecycle-aware components + Kotiln Coroutines
コルーチンを使う際に必要なキャンセル処理を、Lifecycle-aware componentsの一部であるLiveData-KTXを使って実装します。明示的にキャンセル処理を実装する必要が無くなります。
準備
dependencies {
def lifecycle_version = "2.2.0-alpha02"
...
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}
UseCase
UseCaseクラスはビジネスロジックの実装をします。
abstract class UseCase<in P, R> {
private var currentLiveData: LiveData<State<R>>? = null
operator fun invoke(parameters: P, mediator: MediatorLiveData<State<R>>) {
currentLiveData?.let { mediator.removeSource(it) }
// ktx
val ld = liveData {
emit(State.Loading)
try {
emit(State.Success(withContext(Dispatchers.IO) { execute(parameters) }))
} catch (e: Exception) {
Timber.w(e)
emit(State.Failure(e))
}
}
mediator.addSource(ld) {
mediator.value = it
}
currentLiveData = ld
}
abstract suspend fun execute(parameters: P): R
}
class GetTodoListUseCase(
private val todoRepository: TodoRepository
) : UseCase<Unit, List<Todo>>() {
override suspend fun execute(parameters: Unit): List<Todo> {
// ビジネスロジックを記載する(ex. 取得したリストの1~3番目だけ加工する...etc)
return todoRepository.getNotDoneTodoList()
}
}
liveData {}
が肝となる箇所です。
これで生成するLiveDataはinactive時にキャンセル処理を行ってくれます。また、値の通知にはemit(value)
を利用します。※Stateはローディング中・処理完了・処理失敗の状態管理クラスです。
また、UseCaseは再利用(parametersを変えて再リクエスト)可能にしたいので、リクエスト時にMediatorLiveData
を渡し、先程生成したLiveDataを追加しています。(既にリクエストがあった場合は破棄してから追加)
ViewModel
続いてViewModelの実装です。受け取ったリストをLiveDataにセットします。(表示はViewに任せましょう)
class TodoListViewModel(
getTodoListUseCase: GetTodoListUseCase
) : ViewModel() {
private val _todoList = MediatorLiveData<State<List<Todo>>>()
val infoType = MediatorLiveData<InfoType>()
val todoList = MediatorLiveData<List<Todo>>()
init {
infoType.addSource(_todoList) { state ->
infoType.value = when (state) {
is State.Loading -> InfoType.Progress
else -> InfoType.Transparent
}
}
todoList.addSource(_todoList) { state ->
(state as? State.Success)?.result?.let { todoList.value = it }
}
getTodoListUseCase(Unit, _todoList)
}
}
_todoList
はUseCaseの実行結果を受け取るためのLiveDataです。
todoList
は表示用のLiveDataですが、_todoList
の変更を監視したいので、MediatorLiveData
としています。
これまではViewModel.onCleared()
にて、UseCase等のコルーチンをキャンセルする必要がありましたが、LiveDataが管理しているのでViewModelでの記載は不要となります。
おわりに
ViewModelにて、liveData {} を利用して直接データ操作する手法も散見しますが、UseCaseに分けることでボイラープレートが減ると思います。お試しください。
※筆者はViewModelでemitしたり、showLoading()/hideLoading()したくない派です