SingleLiveEvent的问题以及解决方法
为什么会有SingleLiveEvent
在 Android 开发中常用的一个JetPack的组件就是 LiveData,可以很方便地将数据和UI的生命周期结合起来,实现数据驱动UI更新。
然而使用 LiveData 有一个众所周知的副作用:LiveData的粘性事件,即当一个Observer去订阅LiveData的时候,立刻就会收到一次回调,回调中传递的是订阅LiveData之前的值。
LiveData的粘性可以保证UI的初始化(订阅的时候就会收到数据去初始化UI)以及UI的刷新能够同时得到满足。但在某些场景下,我们可能不希望UI初始化的时候使用旧的数据,这些场景下LiveData的粘性事件就变成了副作用。
针对LiveData的粘性事件,有大佬造了一个好用的轮子 SingleLiveEvent,可以保证只有在订阅LiveData之后更新的数据才会回调。
SingleLiveEvent的问题
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, object : Observer<T> {
override fun onChanged(t: T?) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
})
}
@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
setValue(null)
}
companion object {
private val TAG = "SingleLiveEvent"
}
}
SingleLiveEvent的代码如上,实现的十分简洁。但使用过 SingleLiveEvent 就会发现有个致命的问题:只有一个Observer能接收到回调。
分析上面的代码可以很清楚地发现这个问题,当第一个Observer收到回调的时候,会去比较mPending的值,如果为 true 则会回调onChanged,并且更新mPending的值。这样就会导致后续其他的Observer在判断mPending的时候都是 false。
SingleLiveEvent的优化
为了解决上述问题,对SingleLiveEvent进行了小优化,直接给出代码如下:
class SingleLiveEvent<T> : MutableLiveData<T>(), Observer<T> {
private var mVersion = 0
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
super.observe(owner, object : Observer<T> {
private val mInitVersion = mVersion
override fun onChanged(value: T) {
if (mVersion > mInitVersion) {
observer.onChanged(value)
}
}
})
}
@MainThread
override fun setValue(t: T?) {
mVersion ++
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
setValue(null)
}
companion object {
private val TAG = "SingleLiveEvent"
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)