注意避坑:LiveData“不起眼”又会遇到的问题 (Tips)
PostValue() 会丢弃老的值
某天我在编写一段逻辑时这样使用了 LiveData 来传数据:
textLiveData.postValue("1")
textLiveData.postValue("2")
结果在回调处只回调了 "2",那么 "1" 去哪了!
看一下 postValue 的源码:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
// 如果 mPendingData 已经有值,postTask 为 false,
// 就不会执行下面的 postToMainThread 任务
postTask = mPendingData == NOT_SET;
// 每次 post 这里的 mPendingData 都会刷新,
// 所以我们总是能得到最新值的回调
mPendingData = value;
}
if (!postTask) {
return;
}
// 将任务 post 到主线程 Handler 执行
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
// 设置值之后将 mPendingData 重置
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
// 最终还是调的 setValue
setValue((T) newValue);
}
};
postValue 是把值 post 到主线程来执行,而我们的主线程是取队列里的消息一个接一个处理的,所以处理不会那么及时。分析一下上面的场景,先 post 了一个 "1",此时 mPendingData = "1"
,postTask = true
,此任务被 post 给主线程等待执行。紧接着又 post "2",此时 mPendingData = "2"
,postTask = false
,只会修改一下 mPendingData 的值,而主线程的任务在 mPendingData 修改为 "2" 之后才被执行。所以我们得到的回调总是最后一个。
总结: postValue 其实是为多线程环境设计的,普通的场景没有必要使用它。直接使用 setValue 就行。
生命周期重复回调导致的 LiveData 回调重复
之前我把 LiveData 用来在 Activity 和 Fragment 之间传递点击事件。在 ViewPager 里的 fragment 中使用,一下就炸了,左右切换 Fragment 的时候就触发了点击事件。
由于 LiveData 在组件初始化活跃时总是回去检查是否有值,有值就会回调。所以如果一个 Fragment 注册了 LiveData 的回调,Activity 反复移除再添加 Frgment, 就会回调多次 LiveData 的回调。(注:这里的 LiveData 存在于 Activity 的 ViewModel 中,生命周期比 Fragment 长)
之前我的解决方案是在 fragment onDestory 的时候将 LivaData 置为 null。其实不够优雅,这种短暂性的消息,用完就没有意义的可以封装一下 LiveData:
发送完消息立即置为空,一个转瞬即逝的 LiveData,值不会被保存。这里需要做好对 null 的判断: if(v == null) return
class FleetingLiveData<T> : MutableLiveData<T>() {
override fun setValue(value: T) {
super.setValue(value)
super.setValue(null)
}
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, Observer {
if (it == null) return@Observer
observer.onChanged(it)
})
}
}
总结:LiveData 和 组件生命周期绑定,有值就会回调的特性有利有弊。可以用来做粘性事件,但是用不好也会发生奇奇怪怪的回调,比如用来传递点击事件这种不需要保存值的短暂事件。
看完点赞,养成习惯,微信搜一搜「 程序猿养成中心 」关注这个喜欢写干货的程序员。
另外更有Android一线大厂面试完整考点、资料更新在我的Gitee,有面试需要的朋友们可以去参考参考,如果对你有帮助,可以点个Star哦!
Gitee地址:【 老陈的Gitee】
本文地址:https://blog.csdn.net/qq_39477770/article/details/108851836