美文网首页
Android 解决 LiveData 粘性特性的思路

Android 解决 LiveData 粘性特性的思路

作者: 雁过留声_泪落无痕 | 来源:发表于2023-07-12 15:54 被阅读0次

背景

众所周知,LiveData 具有粘性特性,也即先 setValue/postValue 再 observe 也会收到 onChnaged 回调

一种解决思路

要解决粘性,不就是要判断是 setValue/postValue 和 observe 的先后关系吗?遂考虑用时间来进行判断,代码如下:

class NotStickyLiveData<T> : MutableLiveData<T> {

    constructor(value: T) : super(value)

    constructor() : super()

    private var mNewValueTime = 0L
    private val mObserverObserverMap = ConcurrentHashMap<Observer<in T>, Observer<T>>()
    private val mOwnerObserversMap =
        ConcurrentHashMap<LifecycleOwner, CopyOnWriteArrayList<Observer<in T>>>()
    private val mObserverOwnerMap = ConcurrentHashMap<Observer<in T>, LifecycleOwner?>()

    @MainThread
    override fun setValue(value: T) {
        mNewValueTime = System.currentTimeMillis()
        super.setValue(value)
    }

    override fun postValue(value: T) {
        mNewValueTime = System.currentTimeMillis()
        super.postValue(value)
    }

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        if (mObserverObserverMap.containsKey(observer)) {
            if (mObserverOwnerMap[observer] == null) {
                // 说明在 observeForever 中注册过
                throw IllegalArgumentException("Cannot add the same observer with different lifecycles")
            } else if (mObserverOwnerMap[observer] != owner) {
                // 说明在 observe 中注册过但是 owner 不同
                throw IllegalArgumentException("Cannot add the same observer with different lifecycles")
            } else {
                // 说明在 observe 中注册过,不再二次注册
                return
            }
        }

        val time = System.currentTimeMillis()
        val newObserver = Observer<T> {
            if (mNewValueTime > time) {
                observer.onChanged(it)
            }
        }

        mObserverObserverMap[observer] = newObserver
        mObserverOwnerMap[observer] = owner

        synchronized(this) {
            if (mOwnerObserversMap[owner] == null) {
                mOwnerObserversMap[owner] = CopyOnWriteArrayList()
            }
        }
        mOwnerObserversMap[owner]!!.add(observer)

        super.observe(owner, newObserver)
    }

    override fun observeForever(observer: Observer<in T>) {
        if (mObserverObserverMap.containsKey(observer)) {
            if (mObserverOwnerMap[observer] != null) {
                // 说明在 observe 中注册过
                throw IllegalArgumentException("Cannot add the same observer with different lifecycles")
            } else {
                // 说明在 observeForever 中注册过,不再二次注册
                return
            }
        }

        val time = System.currentTimeMillis()
        val newObserver = Observer<T> {
            if (mNewValueTime > time) {
                observer.onChanged(it)
            }
        }

        mObserverObserverMap[observer] = newObserver
        mObserverOwnerMap[observer] = null

        super.observeForever(newObserver)
    }

    override fun removeObserver(observer: Observer<in T>) {
        mObserverObserverMap[observer]?.let {
            mObserverObserverMap.remove(observer)
            super.removeObserver(it)
        }

        mObserverObserverMap.remove(observer)
        mObserverOwnerMap.remove(observer)
    }

    override fun removeObservers(owner: LifecycleOwner) {
        mOwnerObserversMap[owner]?.let {
            mOwnerObserversMap.remove(owner)
            for (observer in it) {
                removeObserver(observer)
            }
        }
    }

}

思考

之所以提供这种思路,是因为有的开发者会把 LiveData 当成 EventBus 来使用,然而 Google 的初衷却只是用来更新 UI 的,当你 observe 时把最新的数据给你让你在 UI 上展示最新的状态也是合情合理的。

顺便提一下,多次 postValue 可能有些数据会“丢失”(当还没来得及把数据回调出去又被设置了更新的数据进来,就会直接用新数据覆盖老数据),其实这也是合理的,因为更新 UI 的状态可能赶不上你更新数据的频率,毕竟需要将最后的回调切换到 UI 线程上去执行。

其它

也许不是最好的方式,也许还存在问题,欢迎指正

相关文章

网友评论

      本文标题:Android 解决 LiveData 粘性特性的思路

      本文链接:https://www.haomeiwen.com/subject/iokpudtx.html