안드로이드

SingleLiveEvent에 대해 알아보자 (사용법)

코딩하는후운 2024. 2. 14. 10:49
반응형

SingleLiveEvent란?

 

LiveData를 사용하던 도중 화면에 진입했을 때 observe가 되면서 재호출이 되는 경우가 있다.
예) 휴대폰 설정 후 화면 진입시 LifeCycle에 의해 다시 호출 등

View의 재활성화 (start나 resume 상태로 재진입)가 되면서 LiveData가 observe를 호출하여,
불필요한 Observer Event까지 일어나는 경우가 있습니다.

이를 방지하기 위해 기존 LiveData를 상속하여 만들어낸 것이 SingleLiveEvent입니다.

 

여러가지 소스 방법이 있는것 같다. (파일명만 다름. 원리는 다 같을겁니다)

SingleLiveEvent

class SingleLiveEvent<T> : MutableLiveData<T>() {
    private val pending = AtomicBoolean(false)
    
    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }
        // Observe the internal MutableLiveData
        super.observe(owner, Observer { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    @MainThread
    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }
    
    @MainThread
    fun call() {
        value = null
    }
    companion object {
        private val TAG = "SingleLiveEvent"
    }
}

SingleEvent

open class SingleEvent<T>(value: T) {

    var value = value
        private set

    private var isAlreadyHandled = false

    fun isActive(): Boolean = if (isAlreadyHandled) {
        false
    } else {
        isAlreadyHandled = true
        true
    }
}

fun <T> LiveData<SingleEvent<T>>.observeEvent(owner: LifecycleOwner, observer: Observer<T>) =
    observe(owner) {
        if (it.isActive()) {
            observer.onChanged(it.value)
        }
    }

fun MutableLiveData<SingleEvent<Unit>>.emit() = postValue(SingleEvent(Unit))

fun <T> MutableLiveData<SingleEvent<T>>.emit(value: T) = postValue(SingleEvent(value))

Event

open class Event<out T>(private val content: T) {

    @Suppress("MemberVisibilityCanBePrivate")
    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
}

/**
 * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
 * already been handled.
 *
 * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
 */
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>?> {
    override fun onChanged(event: Event<T>?) {
        event?.getContentIfNotHandled()?.let {
            onEventUnhandledContent(it)
        }
    }
}

사용방법 (SingleEvent활용)

ViewModel.kt

private val _deleteErrMsg = MutableLiveData<SingleEvent<Unit>>()
val deleteErrMsg: LiveData<SingleEvent<Unit>> = _deleteErrMsg
Fragment.kt
deleteErrMsg.observe(this@ExtraServiceSettingDetailFragment) {
	//원하는 동작
}

 

이를 통해 UI 이벤트를 한 번만 처리하고자 할 때 SingleLiveEvent를 사용할 수 있습니다.

주로 MVVM 아키텍처와 함께 ViewModel에서 LiveData를 사용하여 UI와 데이터 간의 통신을 단순화하는 데 활용됩니다.

참조 : https://zladnrms.tistory.com/146

반응형