반응형
MVVM에서 viewModel 이벤트를 받을 수 있는 방법
- LiveData만 사용해서 이벤트 처리하기
- LivieData에 EventFlow를 래핑해서 처리하기
- SingleLiveData로 이벤트 처리하기
- StateFlow, SharedFlow로 이벤트 처리하기
- SharedFlow, Sealed class로 이벤트 처리하기
- SharedFlow & Sealed class & LifeCycle로 이벤트 처리하기
- EventFlow & Sealed class Lifecyle로 이벤트 처리하기
프로젝트 셋업
간단하게 어떻게 프로젝트를 셋업했는지 설명하겠습니다.
UI를 다음과 같이 구성하고
다음과 같은 동작을 실시하게 했습니다.
- 각 버튼을 클릭한다.
- 각각의 liveData 또는 flow 변수에 데이터를 삽입한다.
- 2번 변수에서 observe를 실시하고 observe가 되었을 때 빈 액티비티를 연다.
- 다시 이전 액티비티로 돌아와서 2번 변수에 저장되어 있는 값을 확인한다.
LiveData만 사용해서 데이터 처리하기
ViewModel 구성
class LiveDataViewModel: ViewModel() {
private val _someData = MutableLiveData<Int>()
val someData: LiveData<Int> = _someData
fun setData() {
if (_someData.value == null) {
_someData.value = 0
} else {
_someData.value = _someData.value!! + 1
}
}
}
테스트용 Activity 구성
class FirstActivity : AppCompatActivity() {
private lateinit var binding: ActivityFirstBinding
private val liveDataViewModel: LiveDataViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFirstBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launch {
liveDataViewModel.someData.observe(this@FirstActivity) {
Timber.d("data observed: $it")
startActivity(Intent(this@FirstActivity, SecondActivity::class.java))
}
}
binding.liveDataButton.setOnClickListener {
liveDataViewModel.setData()
}
}
override fun onResume() {
super.onResume()
Timber.d("liveDataViewModel Data: ${liveDataViewModel.someData.value}")
}
}
테스트 결과
액티비티에서 돌아왔지만 기존의 데이터가 그대로 남아있는 것을 확인할 수 있다.
여기서 데이터가 남아있게 하지 않기 위해 EventWrapper를 사용해 본다
LiveData에 EventWrapper를 적용하기
EventWrapper 클래스를 정의하여 화면으로 돌아왔을 때 다시 한번 Observe 되지 않게 하거나
값을 초기화하는 방법은 한 때 Google에서도 사용하는 방법이 있다.
여기서 언급한 EventWrapper 클래스는 다음과 같은 방법으로 정의할 수 있다.
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
위 EventWrapper를 사용할 ViewModel은 다음과 같이 정의한다.
class EventLiveDataViewModel: ViewModel() {
// LiveData에 EventWrapper를 적용한다.
private val _someData = MutableLiveData<Event<Int>>()
val someData: LiveData<Event<Int>> = _someData
fun setData() {
if (_someData.value?.getContentIfNotHandled() == null) {
_someData.value = Event(0)
} else {
_someData.value = Event(_someData.value?.getContentIfNotHandled() as Int + 1)
}
}
}
그리고 테스트할 Activity는 다음과 같이 정의한다. (앞으로는 이 부분 생략)
class FirstActivity : AppCompatActivity() {
private lateinit var binding: ActivityFirstBinding
private val liveDataViewModel: LiveDataViewModel by viewModels()
private val eventLiveDataViewModel: EventLiveDataViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFirstBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launch {
liveDataViewModel.someData.observe(this@FirstActivity) {
Timber.d("data observed: $it")
startActivity(Intent(this@FirstActivity, SecondActivity::class.java))
}
}
lifecycleScope.launch{
eventLiveDataViewModel.someData.observe(this@FirstActivity) {
Timber.d("data observed: ${it.getContentIfNotHandled()}")
startActivity(Intent(this@FirstActivity, SecondActivity::class.java))
}
}
binding.liveDataButton.setOnClickListener {
liveDataViewModel.setData()
}
binding.liveEventDataButton.setOnClickListener {
eventLiveDataViewModel.setData()
}
}
override fun onResume() {
super.onResume()
Timber.d("liveDataViewModel Data: ${liveDataViewModel.someData.value}")
Timber.d("eventLiveDataViewModel Data: ${eventLiveDataViewModel.someData.value?.getContentIfNotHandled()}")
}
}
그리고 eventLiveDataViewModel 버튼을 클릭하면 다음과 같은 로그가 출력된다.
처음 화면을 열었을 때는 null이 출력되고
Observe 된 후 다른 Activity로 갔다가 돌아온 후에도 값이 다시 null이 되어있다.
이는 EventWrapper로 인해 값을 한 번 Observe 했다면 null을 반환하게 하기 때문이다.
getContentIfNotHandled 이 아니라 peekContent을 사용해서 값을 받아온다면 일반적인 LiveData를 사용했을 때와 똑같이 사용할 수 있다.
MVVM에서 viewModel 이벤트를 받을 수 있는 방법 요약
- LiveData만 사용해서 이벤트 처리하기
- LivieData에 EventFlow를 래핑해서 처리하기
다음 포스팅에서 SingleLiveData와 SharedFlow에 대해 알아보겠습니다.
반응형
'안드로이드(kotlin)' 카테고리의 다른 글
MVVM에서 viewModel 이벤트를 받을 수 있는 방법-3 (0) | 2023.04.01 |
---|---|
MVVM에서 viewModel 이벤트를 받을 수 있는 방법-2 (0) | 2023.03.30 |
안드로이드 매니페스트(AndroidManifest)의 역할은 무엇일까 (0) | 2023.03.22 |
안드로이드 MVVM을 사용하기 위한 필수 요소 AAC란 무엇인가? (0) | 2023.03.21 |
안드로이드 MVVM 아키텍처란 무엇인가 필요성과 그 배경 (0) | 2023.03.17 |
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
댓글