Programming/Android

MVVM에서 ViewModel의 Event 전달하기(Event Wrapper)

YK Choi 2021. 4. 27. 02:40

MVVM Architecture에서 기본 개념 중 하나는 View는 ViewModel을 알지만, ViewModel은 View를 알지 못하게 하자는 것이다. 그로인해 ViewModel은 로직에 집중할 수 있게 된다.

AAC ViewModel에서 작업이 끝나면 LiveData를 통해 observing하는 View가 이를 처리할 수 있게 된다.

 

ViewModel에서 View의 메소드를 가진 인터페이스를 통해 이벤트를 전달했었는데

좋지 않은 방법이라는 것을 깨닫고 LiveData로 이벤트를 핸들링하는 방법을 알게 되었다.

이는 이벤트를 상태의 일부로 처리하는 것이다.

 

liveData가 변경될 때 마다 호출 되는 것이 원래 목적이라면 하던대로 LiveData로 하면 된다.

 

그런데 Snackbar/Toast 메세지, dialog, navigation 이벤트처럼 한 번만 발생해야하는 이벤트를 구분해야 한다면?

또는 한 liveData에 대하여 비동기적으로 read/write하는 서로 다른 함수가 있고 race condition이 발생했을 때 oberver는 이 중 하나만(더 빠른 것) 처리해야 한다면?

이것을 해결하기 위해 일일이 flag를 다는 것도 방법이 될 순 있지만 리더빌리티를 떨어트리는 요인이 될 가능성이 있어서 찜찜하다.

 

이에 대해 한 외국인 개발자분이 잘 정리해 두신 글이 있다.

medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150

 

LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)

2021 Update: Working with Kotlin? I recommend you move to Channels! While we write official guidance, check out this👌 post.

medium.com

 

이 중에 가장 Recommended 방법이라고 하는 Event Wrapper 방식을 사용해보려 했다.

그런데 막상 적용해보려고 하면 중복 호출이 된다.(내 목적은 한번만 처리하는것인데!!)

이때는 알 지 못했다. 

여기선 클릭을 여러번 한 것이기 때문에 의도된 호출이고,

이는 정상적으로 작동한 것이다.

 

 

정의한 Event Class

 

ViewModel의 LiveData

 

View(Activity)의 toastEvent Observer

ViewModel에서 _toastEvent.value = Event(100) 과 같이 데이터를 넣어주면

View의 observer는 getContextIfNotHandled()로 Event의 발생 여부(hasBeenHandled: Boolean)를 true로 만들어 주고 이후의 같은 이벤트는 더 이상 처리하지 않도록 하는 것이 목적이었다.

 

그런데 결과는?

Android Studio의 Logcat

두번째의 호출(위 로그캣의 3번째 줄)부터는 before -> 이후가 true 로 나오면서

새로운 Activity가 실행되지 않아야 하지만 그러지 못하고 있다.

첫번째, 두번째, 세번째의 호출은 서로 각각 다른 이벤트이므로 새로운 Activity가 실행되어야 한다. 

 

.

.

.

 

 

곰곰히 생각을 해보니 ViewModel에서 _toastEvent.value = Event(100)을 할 때의 이 Event(100)은 새로운 객체를 만드는거 아니었나?

외국인 개발자 아저씨는 이렇게 쓰고 있던데 내가 잘못 쓴건가?

내가 잘못 쓴거다

 

이 아저씨에게 박수를 보낸 10.9K명은 정말 써보고 박수를 보낸건가?

박수 받을만 하시다

 

 

Event 객체를 새로 생성하지 않도록 수정을 해보았다.

 

수정한 Event Class

이에 맞춰 ViewModel의 LiveData를 수정하였다.

 

ViewModel의 LiveData

ViewModel에선 아래와 같이 데이터를 넣어주었다. 

 

ViewModel의 데이터 set

 

결과는??

Android Studio의 Logcat

성공,, 바로 내가 원하던 결과였다. Activity도 정상적으로 단 한번만 실행되었다.

이렇게 쓰는게 아니었다.

 

 

10.9K 명이 인정해준 게시글인데 내가 놓친 부분이 있을 것이라 생각이 들어 외국인 개발자 아저씨께 이메일을 보내 보았다.

 

 

찾다보니 알게 됐는데 구글의 안드로이드 엔지니어로 굉장히 유명한 분이셨다.

 

보낸 이메일 내용

메일 쓴거 다시 확인해보니 all every event passed to Activity라고 하면 말이 좀 이상하고(pass까지는 무조건이라서)

Activity에서 이를 handling 되었음을 인식하는게 실패했다고 해야하는데...

 

 

일단 답장을 기다려 보겠다.