LiveData 粘性事件原理详解
一、粘性事件的表现
粘性事件(Sticky Event) 是 LiveData 的核心特性之一,表现为:
-
先发送数据,后注册观察者:即使观察者在数据发送完成后才订阅,仍能接收到最后一次发送的数据。
-
生命周期感知:数据仅在观察者处于活跃状态(如
STARTED或RESUMED)时触发回调。
典型场景:
// 发送数据(先执行)
liveData.postValue("Hello")
// 注册观察者(后执行)
liveData.observe(this) { value ->
Log.d("TAG", "Received: $value") // 仍会输出 "Hello"
}
二、底层实现原理
粘性事件的核心机制依赖于 版本号控制 和 生命周期状态同步,具体流程如下:
1. 版本号机制
-
mVersion:LiveData 内部维护的版本号,初始值为-1。-
发送数据时:每次调用
setValue()或postValue(),mVersion自增。 -
观察者注册时:观察者的
mLastVersion初始化为-1。
-
-
数据分发逻辑:
// considerNotify 方法(关键代码) if (observer.mLastVersion >= mVersion) { return; // 版本一致或更新,不触发回调 } observer.mLastVersion = mVersion; // 更新观察者版本号 observer.mObserver.onChanged(mData); // 触发回调
2. 生命周期绑定
-
LifecycleBoundObserver:包装观察者的核心类,实现LifecycleEventObserver接口。-
状态变化监听:当
LifecycleOwner的状态变为STARTED或RESUMED时,触发activeStateChanged()。 -
数据分发:调用
dispatchingValue(),遍历所有观察者并触发considerNotify()。
-
-
自动触发:观察者注册时,
Lifecycle的ON_CREATE/ON_START事件会立即触发数据分发。
3. 粘性事件触发流程
-
发送数据:
setValue()→mVersion++→dispatchingValue()。 -
注册观察者:
observe()→ 包装为LifecycleBoundObserver→ 添加到mObservers集合。 -
生命周期激活:当组件进入活跃状态时,
LifecycleBoundObserver触发activeStateChanged(true)→ 调用dispatchingValue()。 -
版本对比:
mLastVersion (-1) < mVersion (≥0)→ 触发onChanged(),完成数据传递。
三、源码关键点
1. setValue()与 postValue()
// setValue()
protected void setValue(T value) {
mVersion++; // 版本号自增
mData = value;
dispatchingValue(null); // 触发数据分发
}
// postValue()
protected void postValue(T value) {
// 切换到主线程执行 setValue()
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
2. dispatchingValue()分发逻辑
void dispatchingValue(@Nullable ObserverWrapper initiator) {
do {
if (initiator != null) {
considerNotify(initiator); // 处理指定观察者
initiator = null;
} else {
for (ObserverWrapper wrapper : mObservers.values()) {
considerNotify(wrapper); // 遍历所有观察者
}
}
} while (mDispatchInvalidated);
}
3. considerNotify()版本检查
private void considerNotify(ObserverWrapper observer) {
if (observer.mLastVersion >= mVersion) {
return; // 版本一致,跳过
}
observer.mLastVersion = mVersion; // 更新版本号
observer.mObserver.onChanged(mData); // 触发回调
}
四、粘性事件的应用场景
-
UI 初始化数据同步:
-
Fragment/Activity 重建时自动获取最新数据(如网络请求结果)。
-
-
全局事件总线:
-
跨组件通信时,确保新订阅者接收历史事件(需谨慎使用)。
-
-
配置变更恢复:
-
屏幕旋转后,恢复 ViewModel 中保存的 LiveData 状态。
-
五、粘性事件的问题与解决方案
1. 典型问题
-
数据重复上报:如用户行为日志被多次记录。
-
内存泄漏:旧数据持续触发回调,导致资源浪费。
-
业务逻辑错误:新页面意外接收旧页面的数据。
2. 解决方案
-
Event 包装类:通过标记位控制数据消费次数。
class Event<out T>(private val content: T) { var hasBeenConsumed = false private set fun getContentIfNotConsumed(): T? { return if (hasBeenConsumed) null else content.also { hasBeenConsumed = true } } } -
非粘性 LiveData:自定义 LiveData 子类,覆盖
dispatchingValue()逻辑。class NonStickyLiveData<T> : MutableLiveData<T>() { override fun dispatchingValue(initiator: ObserverWrapper?) { if (initiator == null) return // 仅处理指定观察者 super.dispatchingValue(initiator) } } -
StateFlow
LiveData粘性事件原理解析


2021

被折叠的 条评论
为什么被折叠?



