LiveData 如何避免内存泄漏?
当一个长生命周期的对象引用了一个短生命周期的对象,就会产生内存泄漏。
比如当我们使用
Rxjava来发起一个网络请求的时候,在Activity关闭的时候没有取消订阅,就会发生内存泄漏。
使用 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)
}
}
LiveData 存在的问题?
在使用 LiveData 的过程中,订阅者会收到订阅之前发布的消息。对于一个消息总线来说,这是不可接受的。无论 EventBus 或者 RxBus,订阅方都不会收到订阅之前发出的消息。为什么会存在这个问题呢?
当
LifeCircleOwner的状态发生变化的时候,会调用LiveData.ObserverWrapper的activeStateChanged函数,如果这个时候ObserverWrapper的状态是active,就会调用LiveData的dispatchingValue。
@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);
}
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);
}
}
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
// 🤌 这里
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
// 🤌 这里
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
在 LiveData 的 considerNotify() 方法中,如果 ObserverWrapper 的mLastVersion 小于 LiveData 的 mVersion,就会去回调 mObserver 的onChanged() 方法。而每个新的订阅者,其 version 都是 -1,LiveData 一旦设置过其 version 是大于 -1 的(每次 LiveData 设置值都会使其 version加1),这样就会导致 LiveDataBus 每注册一个新的订阅者,这个订阅者立刻会收到一个回调,即使这个设置的动作发生在订阅之前。
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,其初始的 version 是 -1,当我们调用了其 setValue 或者postValue,其 version 会 +1;对于每一个观察者的封装 ObserverWrapper,其初始 version 也为 -1,也就是说,每一个新注册的观察者,其 version 为-1;当 LiveData 设置这个 ObserverWrapper 的时候,如果 LiveData 的 version 大于 ObserverWrapper 的 version,LiveData 就会强制把当前 value 推送给该 Observer。
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
如何解决 刚订阅就收到之前发送的值 这个问题呢?
明白了问题产生的原因之后,我们来看看怎么才能解决这个问题。很显然,根据之前的分析,只需要在注册一个新的订阅者的时候把
ObserverWrapper的 version 设置成跟LiveData的 version 一致即可。
解决问题的思路是:
无论 ObserverWrapper 还是 LifecycleBoundObserver 都是私有的或者包可见的,所以无法通过继承的方式更改 LifecycleBoundObserver 的 version。
那么能不能从 Map 容器 mObservers 中取到 LifecycleBoundObserver,然后再更改 version 呢?答案是肯定的,通过查看 SafeIterableMap 的源码我们发现有一个 protected 的 get 方法。因此,在调用 observe 的时候,我们可以通过反射拿到 LifecycleBoundObserver ,再把 LifecycleBoundObserver 的 version 设置成和 LiveData 一致即可。
为什么 LiveData 可以自动取消订阅?
如果一个 Observer 的生命周期处于 STARTED 或 RESUMED 状态,那么LiveData 将认为这个 Observer 处于活跃状态,LiveData 仅通知活跃的 Observer 去更新 UI。非活跃状态的 Observer,即使订阅了 LiveData,也不会收到更新的通知。
当页面销毁时,在 mObservers 中 remove 了 observer,就这样完成了订阅的自动取消。
@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);
}
感谢:
https://tech.meituan.com/2018/07/26/android-livedatabus.html









网友评论