【带着问题学】关于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一致即可。
PS:关于我
本人是一个拥有6年开发经验的帅气Android攻城狮,记得看完点赞,养成习惯,微信搜一搜「 程序猿养成中心 」关注这个喜欢写干货的程序员。
另外耗时两年整理收集的Android一线大厂面试完整考点PDF出炉,资料【完整版】已更新在我的【Github】,有面试需要的朋友们可以去参考参考,如果对你有帮助,可以点个Star哦!