查看原文
其他

带着问题学LiveData,你应该知道的知识点

RicardoMJiang 鸿洋 2021-10-13

本文作者


作者:RicardoMJiang 

链接:

https://juejin.im/post/6892704779781275662

本文由作者授权发布。


1前言


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


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


下面列出先列出几个问题


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


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


2LiveData如何避免内存泄漏?


首先说说为什么会发生内存泄漏


当一个长生命周期的对象引用了一个短生命周期的对象,就会产生内存泄漏。


比如当我们使用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,就这样完成了订阅的自动取消。


3Livedata传相同的值会不会执行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,估计只有无聊的人才会写改这么多次值的代码了。


4LiveDataBus实现


为什么要使用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://tech.meituan.com/2018/07/26/android-livedatabus.html


更多


使用美团的这种方式,依赖于反射,在时间上会慢一些,同时性能上有一些损失。


我还看到网络上有一种基于LiveData,不利用反射实现消息总线的方案,比起美团方案各有利弊,有兴趣的同学可以了解下。MVVM最佳实践之UnPeek-LiveData

https://github.com/KunMinX/UnPeek-LiveData




最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!



推荐阅读


没见过这么详细的,自定义控件图形探索!
再见 SharedPreferences,拥抱 Jetpack DataStore
反思 | Android 事件分发的“另一面”!



扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存