1. 问题描述
1.1. LiveData特性:
Google官方文档中描述,设备横竖屏切换的时候,界面销毁重建,但是Activity生命周期并未结束,旋转后新建的空页面上数据需要重新填充,所以LiveData在被再次观察时会立即推送数据更新。
Google文档
ViewModel生命周期
未使用LiveData效果演示
使用LiveData效果演示
1.2. 源码分析:
LiveData关于此特性的源码分析,有兴趣的可以看看下面的文章:
Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus
UnPeekLiveData
1.3. 如果多个界面共用一个ViewModel(其生命周期大于绑定的所有界面),并且复用了同一个LiveData数据源,LiveData数据源在前一个界面被更新过,那么下一个界面在刚刚绑定观察该LiveData时,就会被通知数据发生变化,LiveData最新值会立即推送过来,在一部分场景下可能显得多此一举,或者引发错误。
2. 个人思考总结
2.1. 如果没有多个界面共用同一个ViewModel的需求,或者经常需要用到粘性特性,则无需特殊处理。
2.2. 如果不想使用LiveData的粘性特性,需要自己写一个LiveData继承类来代替MutableLiveData,通过反射或者逻辑判断来规避掉粘性特性(细节可以看上面的源码分析文章)。我自己也写了个很简洁的LiveData实现类,通过判断数据源有无被更改过,控制是否将订阅后的第一次推送通知到界面。代码如下:
public class CleanLiveData<T> extends LiveData<T> {
private boolean hasModified = false;
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull final Observer<? super T> observer) {
super.observe(owner, new Observer<T>() {
private boolean hasIntercept = false;
@Override
public void onChanged(T t) {
if (!hasModified || hasIntercept) {
observer.onChanged(t);
}
hasIntercept = true;
}
});
}
@Override
public void observeForever(@NonNull final Observer<? super T> observer) {
super.observeForever(new Observer<T>() {
private boolean hasIntercept = false;
@Override
public void onChanged(T t) {
if (!hasModified || hasIntercept) {
observer.onChanged(t);
}
hasIntercept = true;
}
});
}
@Override
public void setValue(T value) {
super.setValue(value);
hasModified = true;
}
@Override
public void postValue(T value) {
super.postValue(value);
hasModified = true;
}
}
2.3. Demo项目地址:
LiveDataTest