SingleLiveEvent
SingleLiveEvent는 한 번에 하나의 관찰자만 관찰하는 MutableLiveData의 하위 클래스로 뷰의 생명주기를 인식한다. 이를 사용하는 이유는 액티비티의 화면 회전과 같은 변화(Configuration Change)가 발생하여 onStart/onResume이 다시 호출되는 일이 발생하면 LiveData를 여러번 구독하게 되는 문제점을 보완하기 위해 SingleLiveEvent를 활용한다.
SingleLiveEvent는 코드 내부에서 하나의 observer만 구독 가능하게 구현된다. 만약, 여러개의 observer가 구독할 경우 어느 곳에서 실행될 지 알 수 없다. (MainActivity에서 특정 SingleLiveEvent를 관찰하고 SubActivity에서도 관찰하게 시킨다면 어느 한 곳만 구독이 된다.)
open class SingleLiveEvent<T>() : MutableLiveData<T>() {
//동시성을 보장하는 AtomicBoolean.
private val mPending = AtomicBoolean(false)
/**
* View(Activity or Fragment 등 LifeCycleOwner)가 활성화 상태가 되거나
* setValue로 값이 바뀌었을 때 호출되는 observe 함수.
*
* 아래의 setValue를 통해서만 pending이 true로 바뀌기 때문에,
* 구성에 대한 변화가 일어나도 pending은 false이기 때문에 observe가
* 데이터를 전달하지 않는다!
*/
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Timber.w("Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, Observer { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
/**
* setValue : UI Thread, 즉 Main Thread에서 실행
*
* LiveData로써 들고있는 데이터의 값을 변경하는 함수.
* 여기서는 pending(AtomicBoolean)의 변수는 true로 바꾸어
* observe내의 if문을 처리할 수 있도록 하였음.
*/
@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
// postValue : Background Thread에서 처리 (기존과 같음)
override fun postValue(value: T) {
super.postValue(value)
}
//T가 void일 경우에 활용
@MainThread
fun call() {
value = null
}
companion object {
private val TAG = "SingleLiveEvent"
}
}
CompareAndSet
운영체제에서 흔히 배우는 동시성을 제공하는 타입에서 활용가능한 API로 예상한 값(expected)과 같을 경우에 갱신 값(update)로 변경한다. 값이 변경된 경우 true를 리턴한다.
compareAndSet(boolean expect, boolean update)
즉 아래 코드에서
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
mPending이 true이면 false로 바꾸고 true를 리턴하게 되어 데이터의 변화를 발행한다.
만약 false였다면 expected(true)와 맞지 않아 false를 리턴하게 되어 데이터의 변화를 발행하지 않을 것이다. 즉, setValue를 한 경우에만 mPending이 true인 상태이기 때문에 observe 내의 if문을 통과할 수 있다.
setValue() vs postValue()
- setValue()는 메인 스레드에서 값을 set하는 함수이다.
- postValue()는 백그라운드 스레드에서 값을 set하는 함수이며 이 요청을 main Looper로 전달하기 때문에 결국 메인 스레드에서 값이 변경되며, 내부적으로 setValue()를 호출한다.
결국 위에서 postValue를 호출하더라도 내부적으로는 setValue가 호출되기 때문에 mPending이 바뀌게 된다.
유의사항
위에서 언급한 것 처럼 여러 관찰자를 둘 수 없으므로 한 뷰모델을 여러 프래그먼트가 공유하는 등과 같은 상황에서는 명시적으로 관찰자를 제거해주어야 한다.
How to use SingleLiveEvent in MVVM + Architecture Component. | by Abhishek Tiwari | Medium
참고
'안드로이드' 카테고리의 다른 글
[안드로이드] Dialog와 DialogFragment (0) | 2021.07.08 |
---|---|
[안드로이드] ListAdapter의 작동 원리 및 갱신이 안되는 경우 (2) | 2021.07.02 |
[Android] AAC의 Paging을 사용하지 않고 Paging 처리 하기 (0) | 2021.05.29 |
[Android] Dagger 관련 스택 오버 플로우 질의 번역 - subcomponent with contibutor (0) | 2021.05.05 |
[Android] Dagger2 주요 개념 총 정리 (0) | 2021.05.01 |