有一次面试时被问到什么是LiveData的数据倒灌,当时一脸问号,LiveData倒是经常用,这个词真是没听说过!结果被面试官狠狠的鄙视了一顿,甚至不愿过多解释,直接下一个问题!有点无语!
结果一查不就是Livedata的粘性消息嘛!也不知道是谁又造了这么个词儿,这一天天的!
LiveData在被订阅时,如果已经有旧数据了,则会把旧数据分发给新订阅者。这会导致处理一次性消息时(比如弹窗,Toast),有可能会触发多次。
这其实不算是Livedata的问题,LiveData设计出来就是为了保证数据状态和用户界面一致性的,用来处理一次性消息就有点为难它啦。
不过没关系,只需超简单封装下,就能弥补这个问题!
要想消息只分发一次有两种封装思路:
1. 除去粘性特性 // SingleLiveData类
优点:支持多Observe订阅、调用方式与LiveData保持一致
缺点:没有了消息的粘性特性,导致一些需要粘性特性的场景不能使用。(比如application初始化时请求全局配置接口提前拿到配置数据,但需要等到进入首页才弹升级对话框)
思路简介:再订阅数据时记录一个时间点,再数据更新时记录一个时间点,分发数据时进行时间点判断,只有订阅者订阅的时间早于数据更新的时间,才会被分发数据,即实现了订阅数据时旧数据不会分发给订阅者。
2. 记录订阅者ID // SingleLiveDataSticky类
优点:支持多Observe订阅,保留了粘性特性。
缺点:订阅数据方法,需要多传入一个订阅者ID。
思路简介:引入订阅者ID概念(为每一个订阅者分配一个唯一的ID),分发数据时先判断这个订阅者是否分发过数据,没有分发过则开始分发,然后记录该订阅者的分发状态。若已经分发过就不再分发了。
使用方法:
SingleLiveData使用方式与原始Livedata保持一致,就不示例啦。
封装逻辑比较简单,就不赘述啦,源码如下:
SingleLiveData实现:
/**
* 作用:除去MutableLiveData自带的粘性事件特性,支持多Observe监听,使用方法和MutableLiveData保持一致。
* 原理:通过记录订阅时间点、数据更新时间点,订阅操作之后更新的数据才会分发。
*/
public class SingleLiveData<T> {
private final MutableLiveData<SingleEvent<T>> realLiveData = new MutableLiveData<>();
private final HashMap<Observer<T>, TimeObserverWrapper> foreverObserverMaps = new HashMap<>();
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
realLiveData.observe(owner, new TimeObserverWrapper(observer));
}
public void observeForever(@NonNull Observer<T> observer) {
TimeObserverWrapper wrapper = new TimeObserverWrapper(observer);
foreverObserverMaps.put(observer, wrapper);
realLiveData.observeForever(wrapper);
}
public void removeObserver(@NonNull Observer<T> observer) {
TimeObserverWrapper observerWrapper = foreverObserverMaps.get(observer);
if(observerWrapper == null) return;
realLiveData.removeObserver(observerWrapper);
}
public void setValue(T value) {
realLiveData.setValue(new SingleEvent<>(value));
}
public void postValue(T value) {
realLiveData.postValue(new SingleEvent<>(value));
}
/**
* 原始Observer的代理类,多增加一个createTime字段,用于记录订阅时间
*/
private class TimeObserverWrapper implements Observer<SingleEvent<T>>{
private final long createTime = SystemClock.elapsedRealtime(); //记录当前订阅操作时间(订阅时创建该对象)
private final Observer<T> hostObserver;
public TimeObserverWrapper(Observer<T> observer){
hostObserver = observer;
}
@Override
final public void onChanged(SingleEvent<T> t) {
if(t == null) return;
if(createTime <= t.createTime){ //订阅者订阅的时间要早于数据更新的时间,才会向其分发数据
if(hostObserver != null){
hostObserver.onChanged(t.getValue());
}
}
}
}
/**
* 原始数据代理类,多增加一个createTime字段,用于记录数据创建/更新的时间。
*/
private static class SingleEvent<T> {
protected long createTime = SystemClock.elapsedRealtime(); //记录数据更新时间(数据更新时创建该对象)
private final T value;
public SingleEvent(T value){
this.value = value;
}
public T getValue(){
return value;
}
}
}
SingleLiveDataSticky实现:
/**
* 作用:数据不更新情况下,对一个订阅者 数据只分发一次(同一个订阅者的再次订阅不会分发旧数据);
* 既实现了一次数据更新只分发一次,也保留了livedata的粘性特性。
* 原理:每一个订阅者分配一个固定唯一的ID,数据分发后记录该订阅者ID。数据分发时 查询当前订阅者ID如果已经分发过了,就不分发了。
*/
public class SingleLiveDataSticky<T> {
private final MutableLiveData<SingleEvent<T>> realLiveData = new MutableLiveData<>();
private final HashMap<Observer<T>, TimeObserverWrapper> foreverObserverMaps = new HashMap<>();
/**
* 当只有一个Observe订阅者时 可以使用该方法
*/
public void observeDefaultID(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
observe(owner, Integer.MAX_VALUE, observer);
}
public void observe(@NonNull LifecycleOwner owner, int observeID, @NonNull Observer<T> observer) {
realLiveData.observe(owner, new TimeObserverWrapper(observeID, observer));
}
public void observeForever(int observePos, @NonNull Observer<T> observer) {
TimeObserverWrapper wrapper = new TimeObserverWrapper(observePos, observer);
foreverObserverMaps.put(observer, wrapper);
realLiveData.observeForever(wrapper);
}
public void removeObserver(@NonNull Observer<T> observer) {
TimeObserverWrapper observerWrapper = foreverObserverMaps.get(observer);
if(observerWrapper == null) return;
realLiveData.removeObserver(observerWrapper);
}
public void setValue(T value) {
realLiveData.setValue(new SingleEvent<>(value));
}
public void postValue(T value) {
realLiveData.postValue(new SingleEvent<>(value));
}
/**
* 原始Observer代理类,控制数据分发逻辑
*/
private class TimeObserverWrapper implements Observer<SingleEvent<T>>{
private final int observeId; //每一个订阅者对应唯一的订阅ID
private final Observer<T> hostObserver;
public TimeObserverWrapper(int observePos, Observer<T> observer){
this.observeId = observePos;
hostObserver = observer;
}
@Override
final public void onChanged(SingleEvent<T> t) {
if(t == null) return;
if(hostObserver != null && !t.isIdConsumed(observeId)){ //没有被分发过的订阅者 才进行数据分发
hostObserver.onChanged(t.getValue());
t.idConsume(observeId);
}
}
}
/**
* 原始数据代理类,记录订阅者的分发状态。
*/
private static class SingleEvent<T> {
private final HashMap<Integer, Boolean> id_consumeStateMap = new HashMap<>(); //记录订阅者的分发状态
private final T value;
public void idConsume(int id){
id_consumeStateMap.put(id, true);
}
public boolean isIdConsumed(int id){
return Boolean.TRUE.equals(id_consumeStateMap.get(id));
}
public SingleEvent(T value){
this.value = value;
}
public T getValue(){
return value;
}
}
}
使用建议:
如果一次性消息不需要粘性特性,使用SingleLiveData,使用简单。
如果一次性消息需要粘性特性,使用SingleLiveDataSticky,需要为每一个订阅者分配一个固定唯一的ID;只有一个订阅者也可以使用无ID参数的observeDefaultID()方法,就不需要传入订阅者ID啦。
工程完整地址:github.com/High-Power-…
欢迎评论区交流!!!