阅读 2423

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

前言

带着问题学习可以让我们在学习的过程中更加有目的性与条理。 例如在读源码的过程中,我们如果从头开始读,往往千头万绪,抓不住要领。 而如果在开始读之前先带着几个问题,则可以让我们在读源码的过程中有一个主线。 最后也可以通过判断自己的问题是否得到解决了,来判断自己是否真的读懂了,不然面对成千上万行的代码,很容易有白读了的感觉。

下面列出先列出几个问题
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一致即可。

具体实现

具体实现可见于美团分享的文章,链接在文末

更多

使用美团的这种方式,依赖于反射,在时间上会慢一些,同时性能上有一些损失。
我还看到网络上有一种基于LiveData,不利用反射实现消息总线的方案,比起美团方案各有利弊,有兴趣的同学可以了解下。 MVVM最佳实践之UnPeek-LiveData

参考资料

Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus
UnPeek-LiveData

文章分类
Android
文章标签