Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wl0073921 opened this issue May 14, 2021 · 8 comments
Open

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

wl0073921 opened this issue May 14, 2021 · 8 comments
Labels

Comments

@wl0073921
Copy link

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

@KunMinX KunMinX closed this as completed May 17, 2021
@wl0073921
Copy link
Author

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

@KunMinX
Copy link
Owner

KunMinX commented May 17, 2021

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

@wl0073921
Copy link
Author

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

}

@KunMinX
Copy link
Owner

KunMinX commented Jun 16, 2021

@wl0073921

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

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

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

·

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

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

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

@KunMinX KunMinX reopened this Jun 16, 2021
@wl0073921
Copy link
Author

wl0073921 commented Jun 17, 2021

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

@KunMinX
Copy link
Owner

KunMinX commented Jun 17, 2021

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

@KunMinX KunMinX added the 精华 label Jun 19, 2021
@trycatchx
Copy link

逻辑比 V5 清晰了很多。

@trycatchx
Copy link

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

少了这个方法!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants