用过一段时间LiveData就会发现,LiveData会经常多次回调数据。这是因为LiveData存在粘性事件的问题,如果反复注册监听,就会多次回调。
下面讲一下如何避免这种情况.
方法一:
尽量避免这种情况。合理的设计activity/fragment的view model。fragment别只绑定activity的view model,要有自己独立的view model, 一一对应,使得livedata和fragment同生共死。做到activity的view model在fragment逻辑上只起到共享数据的作用。
方法二:
使用SingleLiveEvent,解决粘性事件的问题。 但是有一个缺点,只能有一个观察者。要考虑自己业务的适用性。
其中的机制是用AtomicBoolean记录setValue状态, 只有当setValue被调用后,才会发送事件。发送完毕后设回false。这样阻止了注册时发送数据。
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import java.util.concurrent.atomic.AtomicBoolean;
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private final AtomicBoolean mPending = new AtomicBoolean(false);
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer<? super T> observer) {
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}
方法三:
使用hook version的方法。这是没有办法的办法。但是功能适配性却最好。没有了粘性事件问题。这也是LiveDataBus源码中的方式。
原理是利用LiveData的mVersion和ObserverWrapper的mLastVersion之间的关系。hook的目的是跳过mVersion和mLastVersion比较这一步,使得onChanged可以执行!
初始注册时mLastVersion是-1, 而LiveData如果之前已经设过值,则mVersion大于-1。所以 mVersion> mLastVersion导致了粘性事件。
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//hook的目的是跳过这一步,使得onChanged可以执行!
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
public class BusMutableLiveData<T> extends MutableLiveData<T> {
private Map<Observer, Observer> observerMap = new HashMap<>();
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
super.observe(owner, observer);
try {
hook(observer);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void observeForever(@NonNull Observer<T> observer) {
if (!observerMap.containsKey(observer)) {
observerMap.put(observer, new ObserverWrapper(observer));
}
super.observeForever(observerMap.get(observer));
}
@Override
public void removeObserver(@NonNull Observer<T> observer) {
Observer realObserver = null;
if (observerMap.containsKey(observer)) {
realObserver = observerMap.remove(observer);
} else {
realObserver = observer;
}
super.removeObserver(realObserver);
}
private void hook(@NonNull Observer<T> observer) throws Exception {
//get wrapper's version
Class<LiveData> classLiveData = LiveData.class;
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
fieldObservers.setAccessible(true);
Object objectObservers = fieldObservers.get(this);
Class<?> classObservers = objectObservers.getClass();
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be bull!");
}
Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
//get livedata's version
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//set wrapper's version
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
方法四:
等官方那个傻逼改正。