Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

写了个简版的防止倒灌,不知道行不行 #17

Open
RebornWolfman opened this issue Aug 4, 2021 · 7 comments
Open

写了个简版的防止倒灌,不知道行不行 #17

RebornWolfman opened this issue Aug 4, 2021 · 7 comments
Labels

Comments

@RebornWolfman
Copy link
Contributor

RebornWolfman commented Aug 4, 2021

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();
}

}

@KunMinX
Copy link
Owner

KunMinX commented Aug 9, 2021

@RebornWolfman

感谢你的分享,刚刚测试一番,结果符合 “防止倒灌” 的预期,但没有发现 “简化逻辑” 的作用,

它相较于 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);
  }
}

@RebornWolfman
Copy link
Contributor Author

RebornWolfman commented Aug 10, 2021

如果只是防止倒灌的话,其实是简化了的,只使用一个变量维持一个版本号,效率会更高,占用内存更少;
写这个我是想仿Eventbus发送事件的,EventBus 事件有普通事件和粘性事件,Livedata 本性比较像是粘性事件;为了可以像Eventbus发送普通事件,我就加入observerMap以消除最后事件更新;

@KunMinX
Copy link
Owner

KunMinX commented Aug 10, 2021

@RebornWolfman

你可以贴一下 “仅防止倒灌” 且不需要 ObserverMap 的简版设计,不然口头上说的话,我难以核对和理解。

@RebornWolfman
Copy link
Contributor Author

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);
        }
    }
}

}

@KunMinX
Copy link
Owner

KunMinX commented Aug 10, 2021

@RebornWolfman

明白了,version 这招真是妙,proxy 的 boolean 遍历来重置,离不开 map,

livedata 的 currentVersion 和 proxy 的 version 的对比,还有 Observe 时的保持一致,刚好解决了这个问题,

非常感谢你的这个分享,为此可以邀请你 pull request 上述设计吗?这可以让你的这次分享出现在贡献名单中。开源不是一个人的战斗,我们希望每位贡献者都能出现在贡献者名单中。

@KunMinX KunMinX added the 精华 label Aug 10, 2021
@wangzhanxian
Copy link

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;
}

@KunMinX
Copy link
Owner

KunMinX commented Sep 10, 2021

@wangzhanxian

上述讨论属于对核心思路的交流,仅供参考,

在提交代码时,贡献者已补充了 equals 等逻辑的处理,具体以项目实际最新源码为准。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants