SingleLiveEvent的问题以及解决方法

为什么会有SingleLiveEvent

在 Android 开发中常用的一个JetPack的组件就是 LiveData,可以很方便地将数据和UI的生命周期结合起来,实现数据驱动UI更新。

然而使用 LiveData 有一个众所周知的副作用:LiveData的粘性事件,即当一个Observer去订阅LiveData的时候,立刻就会收到一次回调,回调中传递的是订阅LiveData之前的值。

LiveData的粘性可以保证UI的初始化(订阅的时候就会收到数据去初始化UI)以及UI的刷新能够同时得到满足。但在某些场景下,我们可能不希望UI初始化的时候使用旧的数据,这些场景下LiveData的粘性事件就变成了副作用。

针对LiveData的粘性事件,有大佬造了一个好用的轮子 SingleLiveEvent,可以保证只有在订阅LiveData之后更新的数据才会回调。

SingleLiveEvent的问题

class SingleLiveEvent<T> : MutableLiveData<T>() {
    private val mPending = AtomicBoolean(false)
    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }
        // Observe the internal MutableLiveData
        super.observe(owner, object : Observer<T> {
            override fun onChanged(t: T?) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t)
                }
            }
        })
    }
    @MainThread
    override fun setValue(t: T?) {
        mPending.set(true)
        super.setValue(t)
    }
    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        setValue(null)
    }
    companion object {
        private val TAG = "SingleLiveEvent"
    }
}

SingleLiveEvent的代码如上,实现的十分简洁。但使用过 SingleLiveEvent 就会发现有个致命的问题:只有一个Observer能接收到回调
分析上面的代码可以很清楚地发现这个问题,当第一个Observer收到回调的时候,会去比较mPending的值,如果为 true 则会回调onChanged,并且更新mPending的值。这样就会导致后续其他的Observer在判断mPending的时候都是 false

SingleLiveEvent的优化

为了解决上述问题,对SingleLiveEvent进行了小优化,直接给出代码如下:

class SingleLiveEvent<T> : MutableLiveData<T>(), Observer<T> {

    private var mVersion = 0

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }
        super.observe(owner, object : Observer<T> {
            
            private val mInitVersion = mVersion

            override fun onChanged(value: T) {
                if (mVersion > mInitVersion) {
                    observer.onChanged(value)
                }
            }
        })
    }
    @MainThread
    override fun setValue(t: T?) {
        mVersion ++
        super.setValue(t)
    }
    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        setValue(null)
    }


    companion object {
        private val TAG = "SingleLiveEvent"
    }
}
posted @ 2024-01-22 18:47  jqc  阅读(123)  评论(0编辑  收藏  举报