この記事では、Androidアプリ開発においてFlowとViewModelを利用し、ライフサイクルを意識しつつ効率的にUIイベントを処理する方法について、実例を交えて解説します。
構成要素の抽象化
- Fragment: イベントを受け取り、UI更新を担当
- ViewModel: イベントの発行・ビジネスロジックの実行を担当
- UseCase: ビジネスロジックを提供
- UiEvent: ViewModelからFragmentに渡されるイベント(画面遷移やエラー表示など)
サンプルコード
Fragment側のイベント監視設定
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.uiEvent
.onEach { handleUiEvent(it) }
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED)
.launchIn(viewLifecycleOwner.lifecycleScope)
}
ここでのポイントは、flowWithLifecycleを使ってイベントの受け取りをライフサイクルに合わせることです。
画面が表示中(RESUMED状態)の時のみイベントが処理され、バックグラウンド状態での無駄な処理を防ぐことができます。
ViewModelでのイベント発行(認証処理例)
private val _uiEvent = Channel<UiEvent>()
val uiEvent = _uiEvent.receiveAsFlow()
fun verifyContentAccess(state: ContentAccessState) {
viewModelScope.launch {
val result = verifyContentAccessUseCase(state)
_uiEvent.send(
when (result) {
is ContentAccessResult.Success -> UiEvent.NavigateToContent(state)
is ContentAccessResult.Error -> UiEvent.ShowError(result.message)
}
)
}
}
sealed class UiEvent {
data class NavigateToContent(val state: ContentAccessState) : UiEvent()
data class ShowError(val message: String?) : UiEvent()
}
ここではイベントを発行するのみにして、実際の処理はFragmentで行います。
FragmentでUiEventのハンドリング
private fun handleUiEvent(event: UiEvent) {
when (event) {
is UiEvent.NavigateToContent -> {
navigateToContentScreen(event.state)
}
is UiEvent.ShowError -> {
showErrorMessage(event.message ?: "予期しないエラーが発生しました。")
}
}
}
private fun navigateToContentScreen(state: ContentAccessState) {
val action = CurrentFragmentDirections.actionToContentFragment(state)
findNavController().navigate(action)
}
private fun showErrorMessage(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
まとめ
Androidアプリ開発において Flow と ViewModel を活用した UIイベント処理は、
- ViewModel側でイベント発行(send)とビジネスロジックを分離し、Fragment側でイベント監視とUI処理を担当することで、役割分担が明確になります
- flowWithLifecycleを用いてライフサイクルを意識したイベント収集を行うことで、リソース消費を最適化し、画面の非表示時の無駄な処理を防ぐことができます