写了个简版的防止倒灌,不知道行不行
RebornWolfman opened this issue · 7 comments
public class EventMutableLiveData extends LiveData {
private final AtomicInteger currentVersion = new AtomicInteger(-1);
/**
* 这个标志主要是是否忽略最新值, observe(@NonNull @NotNull LifecycleOwner owner, @NonNull @NotNull Observer<? super T> observer) 才会生效
* 比如从页面1 来到页面2 页面2 再回到页面1 页面1默认是会收到页面2 最新的值,不想收到这个值可以设置为true
*/
private boolean isIgnoreLastObserve;
private final HashMap<Observer<? super T>, ObserverProxy> observerMap = new HashMap<>();
public EventMutableLiveData(boolean isIgnoreLastObserve) {
super();
this.isIgnoreLastObserve = isIgnoreLastObserve;
}
public EventMutableLiveData() {
super();
}
@Override
public void observe(@NonNull @NotNull LifecycleOwner owner, @NonNull @NotNull Observer<? super T> observer) {
if (!observerMap.containsKey(observer)) {
observerMap.put(observer, new ObserverProxy(observer, currentVersion.get(), false));
} else {
Objects.requireNonNull(observerMap.get(observer)).changeVersion(currentVersion.get());
}
super.observe(owner, Objects.requireNonNull(observerMap.get(observer)));
}
@Override
public void observeForever(@NonNull @NotNull Observer<? super T> observer) {
if (!observerMap.containsKey(observer)) {
observerMap.put(observer, new ObserverProxy(observer, currentVersion.get(), true));
}
super.observeForever(Objects.requireNonNull(observerMap.get(observer)));
}
@Override
public void setValue(T value) {
currentVersion.getAndIncrement();
super.setValue(value);
}
@Override
public void postValue(T value) {
currentVersion.getAndIncrement();
super.postValue(value);
}
class ObserverProxy implements Observer<T> {
private final Observer<? super T> observer;
private int mVersion = -1;
private final boolean isForever;
public ObserverProxy(@NotNull Observer<? super T> observer, int mVersion, boolean isForever) {
this.observer = observer;
this.mVersion = mVersion;
this.isForever = isForever;
}
@Override
public synchronized void onChanged(T t) {
if (currentVersion.get() > mVersion) {
observer.onChanged(t);
if (isIgnoreLastObserve && !isForever) {
Set<Map.Entry<Observer<? super T>, ObserverProxy>> set = observerMap.entrySet();
for (Map.Entry<Observer<? super T>, ObserverProxy> item : set) {
item.getValue().changeVersion(currentVersion.get());
}
}
}
}
private void changeVersion(int newVersion) {
this.mVersion = newVersion;
}
}
@Override
public void removeObserver(@NonNull @NotNull Observer<? super T> observer) {
super.removeObserver(observer);
observerMap.remove(observer);
}
public void removeAllObserver() {
Set<Map.Entry<Observer<? super T>, ObserverProxy>> set = observerMap.entrySet();
for (Map.Entry<Observer<? super T>, ObserverProxy> item : set) {
super.removeObserver(item.getValue());
}
observerMap.clear();
}
}
感谢你的分享,刚刚测试一番,结果符合 “防止倒灌” 的预期,但没有发现 “简化逻辑” 的作用,
它相较于 UnPeekLiveData 的 “防倒灌逻辑” 的不同之处主要体现在:
它在 LiveData 处额外引入了 currentVersion,用作 ObserverProxy onChange 时的内部判断,所以它在 setValue 时无需通过遍历 ObserverProxy 来重置 state。
这是我看到的唯一不同的地方,且某种程度上增加了逻辑的复杂度,
因为目前在 UnPeekLiveData 中,只需盯着 ObserverProxy state 这一变量去发生改变,而上述 EventMutableLiveData 需要在 setValue 时兼顾 currentVersion 的自增、在 Observe 时兼顾 mVersion 的对齐。
最后分享一下 “纯 event 用途” 的情况下,UnPeekLiveData V6.1 的简版设计:
public class ProtectedUnPeekLiveData<T> extends LiveData<T> {
private final ConcurrentHashMap<Observer<? super T>, ObserverProxy> observerMap = new ConcurrentHashMap();
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
Observer<? super T> observer1 = getObserverProxy(observer);
if (observer1 != null) {
super.observe(owner, observer1);
}
}
@Override
public void observeForever(@NonNull Observer<? super T> observer) {
Observer<? super T> observer1 = getObserverProxy(observer);
if (observer1 != null) {
super.observeForever(observer1);
}
}
private Observer<? super T> getObserverProxy(Observer<? super T> observer) {
if (observerMap.containsKey(observer)) {
return null;
} else {
ObserverProxy proxy = new ObserverProxy(observer);
observerMap.put(observer, proxy);
return proxy;
}
}
private class ObserverProxy implements Observer<T> {
public final Observer<? super T> target;
public boolean allow;
public ObserverProxy(Observer<? super T> target) {
this.target = target;
}
@Override
public void onChanged(T t) {
if (allow) {
allow = false;
target.onChanged(t);
}
}
}
@Override
protected void setValue(T value) {
for (Map.Entry<Observer<? super T>, ObserverProxy> entry : observerMap.entrySet()) {
entry.getValue().allow = true;
}
super.setValue(value);
}
@Override
public void removeObserver(@NonNull Observer<? super T> observer) {
observerMap.remove(observer);
super.removeObserver(observer);
}
}
如果只是防止倒灌的话,其实是简化了的,只使用一个变量维持一个版本号,效率会更高,占用内存更少;
写这个我是想仿Eventbus发送事件的,EventBus 事件有普通事件和粘性事件,Livedata 本性比较像是粘性事件;为了可以像Eventbus发送普通事件,我就加入observerMap以消除最后事件更新;
你可以贴一下 “仅防止倒灌” 且不需要 ObserverMap 的简版设计,不然口头上说的话,我难以核对和理解。
public class UnPeekLiveData extends MutableLiveData {
private final AtomicInteger currentVersion = new AtomicInteger(-1);
@Override
public void observe(@NonNull @NotNull LifecycleOwner owner, @NonNull @NotNull Observer<? super T> observer) {
super.observe(owner, new ObserverProxy(observer, currentVersion.get()));
}
@Override
public void observeForever(@NonNull @NotNull Observer<? super T> observer) {
super.observeForever(new ObserverProxy(observer, currentVersion.get()));
}
@Override
public void setValue(T value) {
currentVersion.getAndIncrement();
super.setValue(value);
}
@Override
public void postValue(T value) {
currentVersion.getAndIncrement();
super.postValue(value);
}
class ObserverProxy implements Observer<T> {
private final Observer<? super T> observer;
private int mVersion = -1;
public ObserverProxy(@NotNull Observer<? super T> observer, int mVersion) {
this.observer = observer;
this.mVersion = mVersion;
}
@Override
public synchronized void onChanged(T t) {
if (currentVersion.get() > mVersion) {
observer.onChanged(t);
}
}
}
}
明白了,version 这招真是妙,proxy 的 boolean 遍历来重置,离不开 map,
livedata 的 currentVersion 和 proxy 的 version 的对比,还有 Observe 时的保持一致,刚好解决了这个问题,
非常感谢你的这个分享,为此可以邀请你 pull request 上述设计吗?这可以让你的这次分享出现在贡献名单中。开源不是一个人的战斗,我们希望每位贡献者都能出现在贡献者名单中。
public class UnPeekLiveData extends MutableLiveData {
private final AtomicInteger currentVersion = new AtomicInteger(-1); @Override public void observe(@NonNull @NotNull LifecycleOwner owner, @NonNull @NotNull Observer<? super T> observer) { super.observe(owner, new ObserverProxy(observer, currentVersion.get())); } @Override public void observeForever(@NonNull @NotNull Observer<? super T> observer) { super.observeForever(new ObserverProxy(observer, currentVersion.get())); } @Override public void setValue(T value) { currentVersion.getAndIncrement(); super.setValue(value); } @Override public void postValue(T value) { currentVersion.getAndIncrement(); super.postValue(value); } class ObserverProxy implements Observer<T> { private final Observer<? super T> observer; private int mVersion = -1; public ObserverProxy(@NotNull Observer<? super T> observer, int mVersion) { this.observer = observer; this.mVersion = mVersion; } @Override public synchronized void onChanged(T t) { if (currentVersion.get() > mVersion) { observer.onChanged(t); } } }
}
这样也有问题吧,每次都new ObserverProxy会导致重复监听,最好在ObserverProxy重写equals方法,因为在LiveData中mObservers.putIfAbsent这个方法判断是不是已经存在,最终会通过currentNode.mKey.equals(k)进行比较是否是相同的,所以可以重写ObserverProxy的equal方法如下:
@OverRide
public boolean equals(@nullable Object obj) {
return obj!= null && obj.getClass() == ObserverProxy.class && ((ObserverProxy)obj).observer == this.observer;
}