LiveData 사용시 발생했던 이슈에 대해서 정리했습니다.
LoginViewModel.kt
ChoiceAddressFrag.kt(지역선택 프레그먼트)
위의 코드를 보시면 지역선택 프래그먼트에서 ViewModel의 LiveData를 구독 후 관찰하는 값의 변경이 생기면 옵저버가 콜백메서드를 실행시켜서 로그인 화면으로 이동하는 코드를 작성했습니다.
위의 Gif파일을 보시면 처음에는 지역선택 프래그먼트에서 가입완료 버튼을 누르면 로그인화면으로 이동하지만
그 이후에는 이메일인증 프래그먼트에서 다음 버튼을 눌렀을 때 바로 로그인화면으로 이동하는 것을 확인할 수
있습니다.
원인
ChoiceAddressFrag.kt(지역선택 프레그먼트)
원인은 이메일인증 프래그먼트에서 지역선택 프래그먼트로 이동하자마자 이전에 구독했던 ViewModel의 LiveData에서 옵저버가 이벤트를 발생시켜 등록된 콜백 메서드가 실행되어 로그인 화면으로 이동했기 때문입니다.
그러면 왜 LiveData에서 관찰하는 값이 바뀐것이 아닌데 이벤트가 실행되었는지 확인해봅시다.
ViewModel의 라이프사이클
ViewModel LifeCycle의 특징
-위의 그림처럼 AAC의 ViewModel은 Activity의 UI와 관련된 데이터를 보관하기 위해 설계되었습니다.
그러므로 Activity가 화면회전에 의해 onDestory되어도 ViewModel에 있는 데이터는 소멸하지 않습니다.
-ViewModel에 있는 LiveData는 위와 같은 ViewModel의 LifeCycle에 의해 소멸되지 않고 InActive(비활성화) 상태가
됩니다
LiveData의 InActive(비활성화) -> Active(활성화)
-일반적으로 LiveData는 관찰하는 값이 변경되었을 때에만 이벤트를 발생시켜 등록된 콜백메서드를 실행시키지만
예외적으로 LiveData가 InActive -> Active 상태로 변경될 때도 콜백 메서드를 실행시킵니다.
-그러므로 다시 지역선택 프래그먼트로 이동 시 비활성화 되었던 LiveData가 Active상태로 변경되면서
예외적으로 콜백메서드를 실행시키게 된 것 입니다.
EventWrapper를 사용한 해결방법
Event.kt
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
}
LoginViewModel.kt
//쓰기, 읽기, 수정 가능
private val _LiveRegister = MutableLiveData<Event<User>>()
//읽기만 가능
val liveRegister: LiveData<Event<User>>
get() = _LiveRegister
fun requestRegisterUser(user: User) {
//최종적인 API 통신 응답값을 LiveData에 입력
//-Invoke fun 사용
registerUseCase(user, viewModelScope) {
_LiveRegister.postValue(Event(it))
}
}
ChoiceAddressFrag.kt(지역선택 프레그먼트)
private fun subscribeToLiveData() {
//로그인 화면으로 이동
viewModel.liveRegister.observe(viewLifecycleOwner) { event ->
event.getContentIfNotHandled()?.let {
val action =
ChoiceAddressFragDirections.actionChoiceAddressToLogin()
findNavController().navigate(action)
}
}
-위의 코드처럼 Event클래스를 하나 만들어서 LiveData에 Event클래스를 한번 더 넣은 후에
-프래그먼트에서 Event를 받을 때는 event.getContentIfNotHandled()메서드를 사용해서 콜백메서드를 실행시키면 된다.
참고자료
https://seosh817.tistory.com/9
https://woochan-dev.tistory.com/86
https://jaeryo2357.tistory.com/94
(깊이 있게 잘 설명되어 있음)
'개발 시행착오 정리' 카테고리의 다른 글
[안드로이드] 리사이클러뷰 안에 리사이클러뷰 사용시 안에 있는 리사이클러뷰가 터치가 안될 때 (0) | 2023.01.05 |
---|---|
운동친구 구하는 어플 기획에서 이상한 부분 (0) | 2022.02.14 |
[안드로이드] Java서버 실시간 단체 채팅 구현 과정 및 시행착오 정리 (0) | 2022.02.10 |
[안드로이드] 코틀린 WebRTC 화상 통화 구현 진행 과정 및 시행착오 정리 (0) | 2022.02.08 |
[안드로이드] 이미지 여러 장 처리 시행착오 및 구현 과정 (1) | 2022.01.27 |
댓글