美文网首页Android开发
【带着问题学】关于LiveData你应该知道的知识点

【带着问题学】关于LiveData你应该知道的知识点

作者: 码农的书柜 | 来源:发表于2020-11-08 22:09 被阅读0次

    前言

    带着问题学习可以让我们在学习的过程中更加有目的性与条理。例如在读源码的过程中,我们如果从头开始读,往往千头万绪,抓不住要领。而如果在开始读之前先带着几个问题,则可以让我们在读源码的过程中有一个主线。

    最后也可以通过判断自己的问题是否得到解决了,来判断自己是否真的读懂了,不然面对成千上万行的代码,很容易有白读了的感觉。

    下面列出先列出几个问题:

    1.LiveData为什么可以自动取消订阅,如何避免内存泄漏?
    2.LiveData设置相同的值,会不会回调?
    3.LiveData有什么坑?如何实现LiveDataBus?

    本文主要回答了以上3个问题,读者在读完后可以看看相关问题有没有得到解答~

    LiveData如何避免内存泄漏?

    首先说说为什么会发生内存泄漏,当一个长生命周期的对象引用了一个短生命周期的对象,就会产生内存泄漏。比如当我们使用Rxjava来发起一个网络请求的时候,而在Activity关闭的时候没有取消订阅,就会发生内存泄漏。

    所以需要用户使用CompositeDisposable,来在onDestroy时取消订阅

     private var d: Disposable? = null
     private lateinit var ds: CompositeDisposable
    
     override fun onClick(v: View) {
            when (v.id) {
                R.id.btn_start_count -> {
                    d = Observable.interval(1, TimeUnit.SECONDS)
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe {
                                tv_count.text = it.toString()
                                loge(TAG, "count:$it")
                            }
                    ds.add(d!!)
    
                }
            }
     }
    
     override fun onDestroy() {
            super.onDestroy()
            ds.dispose()
        }
    
    

    如上所示:通过compositeDisable可以避免Rxjava发生内存泄漏。但是这种方法依赖于用户手动操作,容易忘记与出现问题。

    但使用LiveData却不用担心这个问题,LiveData订阅后,在页面销毁后可以自动取消订阅

    class MutableLiveDataFragment : Fragment() {
    
        private val changeObserver = Observer<String> { value ->
            value?.let { txt_fragment.text = it }
        }
    
        override fun onAttach(context: Context?) {
            super.onAttach(context)
            getLiveDataA().observe(this, changeObserver)
        }
        // .. some other Fragment specific code ..
    }
    
    

    为什么LiveData可以自动取消订阅?

    如果一个Observer的生命周期处于STARTED或RESUMED状态,那么LiveData将认为这个Observer处于活跃状态.LiveData仅通知活跃的Observer去更新UI。非活跃状态的Observer,即使订阅了LiveData,也不会收到更新的通知。

    结合一个实现了LifecycleOwner接口的对象,你能注册一个Observer。这种结合关系使得当具有生命周期的对象的状态变为DESTROYED时,Observer将被取消订阅。这对于活和片段尤其有用,因为它们可以安全地订阅LiveData对象,而不必担心内存泄漏 - 当活和片段生命周期为DESTROYED时,它们立即会被取消订阅。

    1.首先看看订阅时发生了什么

    @MainThread
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            assertMainThread("observe");
            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
                // ignore
                return;
            }
            LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
            ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
            if (existing != null && !existing.isAttachedTo(owner)) {
                throw new IllegalArgumentException("Cannot add the same observer"
                        + " with different lifecycles");
            }
            if (existing != null) {
                return;
            }
            owner.getLifecycle().addObserver(wrapper);
        }
    
    

    观察者被存放到了mObservers之中,其中存放的是LifecycleBoundObserver类型接下来看看LifecycleBoundObserver类

    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
            @NonNull
            final LifecycleOwner mOwner;
    
            LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
                super(observer);
                mOwner = owner;
            }
    
            @Override
            boolean shouldBeActive() {
                return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
            }
    
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                    removeObserver(mObserver);
                    return;
                }
                activeStateChanged(shouldBeActive());
            }
    
            @Override
            boolean isAttachedTo(LifecycleOwner owner) {
                return mOwner == owner;
            }
    
            @Override
            void detachObserver() {
                mOwner.getLifecycle().removeObserver(this);
            }
        }
    
    

    当Lifecycles的State发生变化会回调onStateChanged方法,当State为DESTROYED是,则移除观察者Observer。里面调用的是LiveData的removeObserver方法

    @MainThread
        public void removeObserver(@NonNull final Observer<? super T> observer) {
            assertMainThread("removeObserver");
            ObserverWrapper removed = mObservers.remove(observer);
            if (removed == null) {
                return;
            }
            removed.detachObserver();
            removed.activeStateChanged(false);
        }
    
    

    当页面销毁时,在mObservers中remove了observer,就这样完成了订阅的自动取消。

    livedata传相同的值会不会执行onchanged回调?

    当我们给liveData设置value时,Observer就会更新。但如果我们两次设置一样的value,Observer是否会更新?

    首先看看setValue方法

    @MainThread
        protected void setValue(T value) {
            assertMainThread("setValue");
            mVersion++;
            mData = value;
            dispatchingValue(null);
        }    
    
    

    其中最重要的是mVersion参数

    接下来看dispatchingValue方法

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
            ....
            considerNotify(initiator);
            ....
        }
    
    private void considerNotify(ObserverWrapper observer) {
            if (!observer.mActive) {
                return;
            }
            // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
            //
            // we still first check observer.active to keep it as the entrance for events. So even if
            // the observer moved to an active state, if we've not received that event, we better not
            // notify for a more predictable notification order.
            if (!observer.shouldBeActive()) {
                observer.activeStateChanged(false);
                return;
            }
            if (observer.mLastVersion >= mVersion) {
                return;
            }
            observer.mLastVersion = mVersion;
            observer.mObserver.onChanged((T) mData);
        }
    
    

    到这里一目了然,只要mVersion是大于等于之前的值,就会回调onChanged方法,也就是说,不管值是否相同,只看version的值,也就是基本只有int到达最大值的时候才会导致bug,2的31次方-1,估计只有无聊的人才会写改这么多次值的代码了。

    LiveDataBus实现

    为什么要使用LiveData来实现通信总线?

    使用LiveData的理由

    • LiveData具有的这种可观察性和生命周期感知的能力,使其非常适合作为Android通信总线的基础构件。

    • 使用者不用显示调用反注册方法。由于LiveData具有生命周期感知能力,所以LiveDataBus只需要调用注册回调方法,而不需要显示的调用反注册方法。这样带来的好处不仅可以编写更少的代码,而且可以完全杜绝其他通信总线类框架(如EventBus、RxBus)忘记调用反注册所带来的内存泄漏的风险。

    为什么要用LiveDataBus替代EventBus和RxBus

    • LiveDataBus的实现及其简单 相对EventBus复杂的实现,LiveDataBus只需要一个类就可以实现。

    • LiveDataBus可以减小APK包的大小 由于LiveDataBus只依赖Android官方Android Architecture Components组件的LiveData,没有其他依赖,本身实现只有一个类。作为比较,EventBus JAR包大小为57kb,RxBus依赖RxJava和RxAndroid,其中RxJava2包大小2.2MB,RxJava1包大小1.1MB,RxAndroid包大小9kb。使用LiveDataBus可以大大减小APK包的大小。

    • LiveDataBus依赖方支持更好 LiveDataBus只依赖Android官方Android Architecture Components组件的LiveData,相比RxBus依赖的RxJava和RxAndroid,依赖方支持更好。

    • LiveDataBus具有生命周期感知 LiveDataBus具有生命周期感知,在Android系统中使用调用者不需要调用反注册,相比EventBus和RxBus使用更为方便,并且没有内存泄漏风险。

    LiveData实现事件总线的坑

    由于LiveData的粘性特性,用LiveData实现LiveDataBus,订阅者会收到订阅之前发布的消息。对于一个消息总线来说,这是不可接受的。无论EventBus或者RxBus,订阅方都不会收到订阅之前发出的消息。对于一个消息总线,LiveDataBus必须要解决这个问题。

    粘性消息的原因

    当LifeCircleOwner的状态发生变化的时候,会调用LiveData.ObserverWrapper的activeStateChanged函数,如果这个时候ObserverWrapper的状态是active,就会调用LiveData的dispatchingValue。

    前面分析过,最后会调用到considerNotify方法

    private void considerNotify(ObserverWrapper observer) {
            if (!observer.mActive) {
                return;
            }
            // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
            //
            // we still first check observer.active to keep it as the entrance for events. So even if
            // the observer moved to an active state, if we've not received that event, we better not
            // notify for a more predictable notification order.
            if (!observer.shouldBeActive()) {
                observer.activeStateChanged(false);
                return;
            }
            if (observer.mLastVersion >= mVersion) {
                return;
            }
            observer.mLastVersion = mVersion;
            observer.mObserver.onChanged((T) mData);
        }
    
    

    在LiveData的considerNotify方法中,如果ObserverWrapper的mLastVersion小于LiveData的mVersion,就会去回调mObserver的onChanged方法。而每个新的订阅者,其version都是-1,LiveData一旦设置过其version是大于-1的(每次LiveData设置值都会使其version加1),这样就会导致LiveDataBus每注册一个新的订阅者,这个订阅者立刻会收到一个回调,即使这个设置的动作发生在订阅之前。

    问题原因总结

    对于这个问题,总结一下发生的核心原因。对于LiveData,其初始的version是-1,当我们调用了其setValue或者postValue,其vesion会+1;对于每一个观察者的封装ObserverWrapper,其初始version也为-1,也就是说,每一个新注册的观察者,其version为-1;当LiveData设置这个ObserverWrapper的时候,如果LiveData的version大于ObserverWrapper的version,LiveData就会强制把当前value推送给Observer。

    如何解决这个问题

    明白了问题产生的原因之后,我们来看看怎么才能解决这个问题。很显然,根据之前的分析,只需要在注册一个新的订阅者的时候把Wrapper的version设置成跟LiveData的version一致即可。

    那么怎么实现呢,看看LiveData的observe方法,他会在observe时创建一个LifecycleBoundObserver,LifecycleBoundObserver是ObserverWrapper的派生类。然后会把这个LifecycleBoundObserver放入一个私有Map容器mObservers中。无论ObserverWrapper还是LifecycleBoundObserver都是私有的或者包可见的,所以无法通过继承的方式更改LifecycleBoundObserver的version。

    那么能不能从Map容器mObservers中取到LifecycleBoundObserver,然后再更改version呢?答案是肯定的,通过查看SafeIterableMap的源码我们发现有一个protected的get方法。因此,在调用observe的时候,我们可以通过反射拿到LifecycleBoundObserver,再把LifecycleBoundObserver的version设置成和LiveData一致即可。

    原文地址:https://juejin.im/post/6892704779781275662

    PS:关于我


    本人是一个拥有6年开发经验的帅气Android攻城狮,记得看完点赞,养成习惯,微信搜一搜「 程序猿养成中心 」关注这个喜欢写干货的程序员。

    另外耗时两年整理收集的Android一线大厂面试完整考点PDF出炉,资料【完整版】已更新在我的【Github】,有面试需要的朋友们可以去参考参考,如果对你有帮助,可以点个Star哦!

    地址:【https://github.com/733gh/xiongfan】

    相关文章

      网友评论

        本文标题:【带着问题学】关于LiveData你应该知道的知识点

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