MVVM에서 viewModel 이벤트를 받을 수 있는 방법
- SingleLiveData로 이벤트 처리하기
- StateFlow, SharedFlow로 이벤트 처리하기
- SharedFlow, Sealed class로 이벤트 처리하기
- SharedFlow & Sealed class & LifeCycle로 이벤트 처리하기
- EventFlow & Sealed class Lifecyle로 이벤트 처리하기
SingleLiveData를 사용해서 데이터 이벤트 받기
기존의 방법인 EventWrapper를 사용하여 viewModel 이벤트를 받는 방법은
매번 liveData 변수를 Wrapping하고 데이터 입출력도 Wrapping을 해야 하기 때문에 상당히 번거로웠다.
그래서 SingleLiveData라는 것을 사용하여 이미 Wrapping된 viewModel을 가져오는 방식이 사용되었다.
SingleLiveData 정의하기
기존에 EventWrapper를 사용하는 것과 동일하다.
abstract class SingleLiveData<T> {
// Event로 래핑
private val liveData = MutableLiveData<Event<T>>()
protected constructor()
protected constructor(value: T) {
liveData.value = Event(value)
}
protected open fun setValue(value: T) {
liveData.value = Event(value)
}
protected open fun postValue(value: T) {
liveData.postValue(Event(value))
}
// 기존에 저장되어있는 값을 가져온다 = 기존과 동일한 방법
fun getValue() = liveData.value?.peekContent()
// 항상 첫번째로 들어간 값만 가져온다
fun getNotHandledValue() = liveData.value?.getContentIfNotHandled()
// 기존에 저장되어있는 값을 관찰한다. = 기존과 동일한 방법
fun observePeek(owner: LifecycleOwner, onResult: (T) -> Unit) {
liveData.observe(owner) { onResult(it.peekContent()) }
}
// 항상 첫 번째로 들어간 값만 관찰한다.
fun observe(owner: LifecycleOwner, onResult: (T) -> Unit) {
liveData.observe(owner) { it.getContentIfNotHandled()?.let(onResult) }
}
}
MutableSingleLiveData 정의하기
SingleLiveData를 사용하기 위한 MutableSingleLiveData를 정의한다.
class MutableSingleLiveData<T> : SingleLiveData<T> {
constructor() : super()
constructor(value: T) : super(value)
public override fun postValue(value: T) {
super.postValue(value)
}
public override fun setValue(value: T) {
super.setValue(value)
}
}
SingleViewModel 정의하기
viewModel 또한 지금까지 정의했던 것과 동일한 방법으로 정의한다.
class SingleViewModel : ViewModel() {
private val _someData = MutableSingleLiveData<Int>()
val someData: SingleLiveData<Int> = _someData
fun setData() {
if (_someData.getNotHandledValue() == null) {
_someData.setValue(0)
} else {
_someData.setValue(_someData.getNotHandledValue()!! + 1)
}
}
}
이렇게 EventWrapper와 SingleLiveData를 사용하여
매 번 초기화된 liveData를 받을지
계속 데이터가 유지되는 liveData를 받을지 선택할 수 있습니다.
SharedFlow를 사용해서 viewModel Event 수신
하지만 liveData만 사용하다 보니 클린 아키텍처를 사용할 때 발생하는 문제가 있습니다.
클린 아키텍처에선 각 Layer는 나뉘어 있고
viewModel은 Presenters Layer에 위치하고 있기 때문에
특정 플랫폼과 관계가 없어야 합니다.
즉, import android.xxx 인 코드가 없도록 유지되어야 한다는 뜻입니다.
해당 글은 구글 직원이 쓴 블로그에도 나와 있는 내용입니다.
Don’t let ViewModels (and Presenters) know about Android framework classe
하지만 지금까지 사용했던 LiveData는 안드로이드 패키지에 해당하는 기술입니다.
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
하지만 sharedFlow를 사용한다면 해당 패키지를 없앨 수 있습니다.
다음과 같이 sharedFlowViewModel을 구현합니다.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
// androidx가 kotlinx 패키지로 교체됨
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
class SharedFlowViewModel : ViewModel() {
private val _someData = MutableSharedFlow<Int>()
val someData = _someData.asSharedFlow()
fun setData() {
viewModelScope.launch {
_someData.emit(1)
}
}
}
이렇게 함으로써 안드로이드 패키지에 의존하지 않고 viewModel을 구성할 수 있게 되었습니다.
그다음 Acitivity or Fragment에서 다음과 같이 정의합니다.
lifecycleScope.launch {
sharedFlowViewModel.someData.collect {
Timber.d("sharedViewModel data observed: $it")
openSecondActivity()
}
}
binding.sharedFlowButton.setOnSingleClickListener {
sharedFlowViewModel.setData()
}
sharedFlow의 경우 get이나 value로 값을 직접 가져올 수 없고
collect를 통해서 emit 되는 값만 받아올 수 있습니다.
직접 값을 가져와야 할 필요가 있다면 stateFlow를 사용하면 됩니다
위 블로그는 아래 블로그를 참조하여 만들었습니다.
'안드로이드(kotlin)' 카테고리의 다른 글
안드로이드 앱 파일 만들기(APK) (0) | 2023.04.26 |
---|---|
MVVM에서 viewModel 이벤트를 받을 수 있는 방법-3 (0) | 2023.04.01 |
MVVM에서 viewModel 이벤트를 받을 수 있는 방법-1 (0) | 2023.03.26 |
안드로이드 매니페스트(AndroidManifest)의 역할은 무엇일까 (0) | 2023.03.22 |
안드로이드 MVVM을 사용하기 위한 필수 요소 AAC란 무엇인가? (0) | 2023.03.21 |
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
댓글