📱 Android

[Android] SingleLiveEvent , EventWrapper

콩드로이드 2023. 9. 3. 16:30

SingleLiveEvent

 : 화면 회전 등 구성요소 변경 시, 이벤트가 여러번 호출되는 걸 방지하기 위해 사용 -> 단일 이벤트 사용

import android.util.Log
import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean

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"
    }
}

 

소스를 간단히 살펴보자면,

pending값이 expectedValue와 동일하다면 newValue로 값을 변경 후 true를 리턴한다 

setValue에서 pending이 true로 세팅 -> observe function에서 true일 때 observer.onChanged(t)가 실행됨

 

단점

- 하나의 observer만 제한됨

- 만약 여러개의 observer가 observe해도 그 중 하나만 호출 (어느 observer가 호출되는지 보장할 수 없음)

 


EventWrapper

 : SingleLiveEvent의 단점을 보완하기 위해 사용, 마찬가지로 단일이벤트 

/**
 * 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
}

 

소스를 살펴보자면, 

hasBeenHandled로 이벤트처리의 유무를 판단 -> false일 때, 이벤트 처리 표시 및 값 반환

 

위 상태에서 Event를 observe하면, 매번 observe마다 

라이브데이터.observer(owner) { event ->
	if(event.getContentIfNotHandled()?) // 매번 해야함 
    //TODO 
}

event.getContentIfNotHandled()를 매번 해야한다는 불편함이 있어서 

보통 EventObserver를 같이 사용합니다 

 

[EventObserver]

import androidx.lifecycle.Observer

class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
    override fun onChanged(event: Event<T>?) {
        event?.getContentIfNotHandled()?.let { value ->
            onEventUnhandledContent(value)
        }
    }
}

 

EventObserver를 사용하면 훨씬 간결해집니다