[Android] SingleLiveEvent의 원리
Android

[Android] SingleLiveEvent의 원리




SingleLiveEvent를 사용하는 이유를 설명할 수 있어야 한다.


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

 

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

(SingleLiveEvent에 대해 상세히 알고싶다면 - 링크)

 

일반적인 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"
    }
}

 

코드만으로는 이해할 수 없으신 분들도 계실 것 같아, 아래에 원리에 대한 설명을 달아보았습니다.

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>() {
    /**
    * 멀티쓰레딩 환경에서 동시성을 보장하는 AtomicBoolean.
    * false로 초기화되어 있음
    */
    private val pending = AtomicBoolean(false)
    
    /**
    * View(Activity or Fragment 등 LifeCycleOwner)가 활성화 상태가 되거나
    * setValue로 값이 바뀌었을 때 호출되는 observe 함수.
    * pending.compareAndSet(true, false)라는 것은, 위의 pending 변수가
    * true면 if문 내의 로직을 처리하고 false로 바꾼다는 것이다.
    *
    * 아래의 setValue를 통해서만 pending값이 true로 바뀌기 때문에,
    * Configuration Changed가 일어나도 pending값은 false이기 때문에 observe가
    * 데이터를 전달하지 않는다!
    */
    @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)
            }
        })
    }

    /**
    * LiveData로써 들고있는 데이터의 값을 변경하는 함수.
    * 여기서는 pending(AtomicBoolean)의 변수는 true로 바꾸어 
    * observe내의 if문을 처리할 수 있도록 하였음.
    */
    @MainThread
    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }
    
    /**
    * 데이터의 속성을 지정해주지 않아도 call만으로 setValue를 호출 가능
    */
    @MainThread
    fun call() {
        value = null
    }
    
    companion object {
        private val TAG = "SingleLiveEvent"
    }
}

즉 postValue나 setValue, call등을 통하여 setValue 함수를 거쳐야만이 Observer을 통하여 데이터를 전달 할 수 있으며,

이는 Configuration Changed 등의 LifeCycleOwner의 재활성화 상태가 와도 원치 않는 이벤트가 일어나는 것을 방지할 수 있도록 해줍니다.