KunMinX / UnPeek-LiveData

LiveData 数据倒灌:别问,问就是不可预期 - Perfect alternative to SingleLiveEvent, supporting multiple observers.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ProtectedUnPeekLiveData为什么不能直接持有Observer?

wl0073921 opened this issue · comments

如题,反正LiveData已经通过mObservers持有了Observer,再多一次持有也无所谓吧?
ProtectedUnPeekLiveData重写removeObserver()清理就好了嘛。

直接持有并不是就一个对象,可以用map来管理呀。
image
第一个map来管理是否拦截倒灌。
第二个map来在removeObserver(observer: Observer)时移除observer。
这样的话,就不需要反射到observers拿observer了。

您可以先 fork 和提交您改进的代码,不然缺乏一致的前提依据来有效交流。

ProtectedUnPeekLiveData自持有observer及其proxy,在removeObserver(observer: Observer)时到observerProxyMap中获取相应的observer,而不是反射mObservers。
class ProtectedUnPeekLiveData : MutableLiveData() {

companion object {
    private const val TAG = "ProtectedUnPeekLiveData"
}

// observer及其是否需要观察变化的映射
private val observerStateMap: ConcurrentHashMap<Observer<in T>, Boolean> = ConcurrentHashMap()
// observer及其代理的映射
private val observerProxyMap: ConcurrentHashMap<Observer<in T>, Observer<in T>> =
    ConcurrentHashMap()

override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
    getObserverProxy(observer)?.let {
        super.observe(owner, it)
    }
}

override fun observeForever(observer: Observer<in T>) {
    getObserverProxy(observer)?.let {
        super.observeForever(it)
    }
}

private fun getObserverProxy(observer: Observer<in T>): Observer<T>? {
    return if (observerStateMap.containsKey(observer)) { // 去重,owner相同时没意义,owner不相同时会crash
        Log.d(TAG, "observe repeatedly, observer has been attached to owner")
        null
    } else {
        observerStateMap[observer] = false
        val proxy = ObserverProxy(observer)
        observerProxyMap[observer] = proxy
        proxy
    }
}

private inner class ObserverProxy(observer: Observer<in T>) : Observer<T> {

    private val target = observer

    override fun onChanged(t: T) {
        if (observerStateMap[target] == true) {
            observerStateMap[target] = false
            target.onChanged(t)
        }
    }

    internal fun getTarget(): Observer<in T> {
        return target
    }
}

open fun observeSticky(owner: LifecycleOwner, observer: Observer<T>) {
    super.observe(owner, observer)
}

open fun observeStickyForever(observer: Observer<T>) {
    super.observeForever(observer)
}

override fun setValue(value: T) {
    for (item in observerStateMap) {
        item.setValue(true)
    }
    super.setValue(value)
}

/**
 * 移除observer
 * @param observer(业务侧触发时是Observer,LiveData内部触发时是ObserverProxy)
 */
override fun removeObserver(observer: Observer<in T>) {
    val proxy: Observer<in T>?
    val target: Observer<in T>?
    if (observer is ObserverProxy) {
        proxy = observer
        target = observer.getTarget()
    } else {
        proxy = observerProxyMap[observer]
        target = if (proxy != null) observer else null
    }
    if (proxy != null && target != null) {
        observerProxyMap.remove(target)
        observerStateMap.remove(target)
        super.removeObserver(proxy)
    }
}

}

@wl0073921

感谢你的分享,刚刚测试了一番,上述代码的设计十分精妙,

对于非粘性的 observe 是当页面离开时即移除 observe,而粘性的 observe 则得以保留,

如此即使不使用反射,也能规避页面重建时,非粘性 observe 的重复创建和内存占用。

·

为此,可以邀请你 pull request 一稿上述的代码设计吗?(作为 V6 版)

上述设计使 UnPeek-LiveData 变得更好,开源并不是一个人的战斗,我们希望越来越多 “对开源项目作出过有效贡献的开发者” 出现在 Contributions 名单中。

(考虑到多数开发者阅读 Java 源码的需要,后续我们会翻译成 Java 代码,和基于 “唯一可信源” 理念对 setValue 等方法的访问权限做些微调。当然你若愿意主动翻译那更好了。)

你这边直接拿去用即可。另,我只是实现了我的一个想法,并没有充分测试哈,小心有坑!

好的,感谢,我们会在 v6 版源码中注明贡献者和出处。

逻辑比 V5 清晰了很多。

  override fun postValue(value: T) {
        for (item in observerStateMap) {
            item.setValue(true)
        }
        super.postValue(value)
    }

少了这个方法!