LiveData的基本使用
创建一个LiveData的类:
object MyLiveData { // 单例
// 懒加载
val info1: MutableLiveData<String> by lazy { MutableLiveData() }
}
在Activity中:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView : TextView = findViewById(R.id.tv_textview)
// 1 观察者 眼睛 环节
MyLiveData.info1.observe(this, {
textView.text = it // 更新UI
})
```
// 手动考虑释放工作
MyLiveData.value1.observeForever({
})
}
override fun onDestroy() {
super.onDestroy()
// 手动释放
// MyLiveData.value1.removeObserver()
}
}
LiveData存在数据黏性的问题,就是先设置值,后订阅也能收到值,这是因为一般的,我们只关心订阅之后的数据变化。但这种行为在某些情况下是有用的,比如配置更改(如屏幕旋转)后恢复UI状态,但在其他场景中可能导致不希望的重复数据更新。 如何解决这个问题呢?可以通过反射来处理:
/**
* 单例模式 去掉黏性事件(有关闭黏性的开关) KT的版本
*/
object OKLiveDataBusKT {
// 存放订阅者
private val bus : MutableMap<String, BusMutableLiveData<Any>> by lazy { HashMap() }
// 暴露一个函数,给外界注册 订阅者关系
@Synchronized
fun <T> with(key: String, type: Class<T>, isStick: Boolean = true) : BusMutableLiveData<T> {
if (!bus.containsKey(key)) {
bus[key] = BusMutableLiveData(isStick)
}
return bus[key] as BusMutableLiveData<T>
}
// Any? 是 Object , * 星投影 KT泛型的? 有点像 Java ?
class BusMutableLiveData<T> private constructor() : MutableLiveData<T>() {
var isStick: Boolean = false
// 次构造
constructor(isStick: Boolean) : this() {
this.isStick = isStick
}
// 我是先执行
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, observer) // 这句会执行父类的 // 启用系统的功能 不写就破坏了
if (!isStick) {
hook(observer = observer)
Log.d("derry", "Kotlin版本的 不启用黏性")
} else {
Log.d("derry", "Kotlin版本的 启用黏性")
}
}
private fun hook(observer: Observer<in T>) {
// TODO 1.得到mLastVersion
// 获取到LivData的类中的mObservers对象
val liveDataClass = LiveData::class.java
val mObserversField: Field = liveDataClass.getDeclaredField("mObservers")
// 私有修饰也可以访问
mObserversField.isAccessible = true
// 获取到这个成员变量的对象 Any == Object
val mObserversObject: Any = mObserversField.get(this)
// 得到map对象的class对象 private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
val mObserversClass: Class<*> = mObserversObject.javaClass
// 获取到mObservers对象的get方法 protected Entry<K, V> get(K k) {
val get: Method = mObserversClass.getDeclaredMethod("get", Any::class.java)
// 私有修饰也可以访问
get.isAccessible = true
// 执行get方法
val invokeEntry: Any = get.invoke(mObserversObject, observer)
// 取到entry中的value is "AAA" is String is是判断类型 as是转换类型
var observerWraper: Any? = null
if (invokeEntry != null && invokeEntry is Map.Entry<*, *>) {
observerWraper = invokeEntry.value
}
if (observerWraper == null) {
throw NullPointerException("observerWraper is null")
}
// 得到observerWraperr的类对象
val supperClass: Class<*> = observerWraper.javaClass.superclass
val mLastVersion: Field = supperClass.getDeclaredField("mLastVersion")
mLastVersion.isAccessible = true
// TODO 2.得到mVersion
val mVersion: Field = liveDataClass.getDeclaredField("mVersion")
mVersion.isAccessible = true
// TODO 3.mLastVersion=mVersion
val mVersionValue: Any = mVersion.get(this)
mLastVersion.set(observerWraper, mVersionValue)
}
}
}
使用方式:
源码解析
粘性数据的由来
- LiveData 内部维护了一个全局版本号
mVersion,每次setValue()(或最终的postValue())都会自增。 - 每个观察者对应一个
ObserverWrapper.mLastVersion,记录它上次接收数据时的版本。 - 当新观察者注册时,它的
mLastVersion默认为初始值(–1),必然小于 当前的mVersion,因此注册时就会触发一次分发,把“老数据”推给它。
LiveData为为什么能够观察数据的变化,这与他实现 LifecycleBoundObserver 是分不开的。实现这个接口,让他又能能够感知被观察者生命周期的能力。
在 LiveData 中有下边这个重要的函数,状态的变化分发都是通过他进行的
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
shouldBeActive 判断了当前被观察者(Activity, Fragment)的生命周期至少是STARTED才会继续进行数据的分发
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
dispatchingValue来实现真正的数据分发。
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
dispatchingValue进入后
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// 该值为空的时候,走的是setValue和postValue,不为空的时候,是在状态变化的时候进行的分发
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
considerNotify又进行了状态检测,这是为什么呢?因为页面生命周期变化可能很快,我们要实时的检测他的状态。
private void considerNotify(ObserverWrapper observer) {
// 实时检测状态,防止获取状态不对。
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
// 处理页面迅速恢复的情况。
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
// 决定黏性事件的执行与非执行,如果我们先设置了值,那么这里的mLastVersion会在setvalue的时候执行++,导致这两个值不相等,进而会走分发。去掉粘性也很简单,只需要通过反射让这两个值相等就行了。
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
setValue与postValue
postValue
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
可见postValue与setValue的区别就是一个线程的切换。因此如果你需要在子线程使用livedata,需要用postValue来实现更新。
再次回调dispatchingValue,这次的initiator事空值,为什么else分支里边是一个循环呢?这是因为对于同一个变量,可能有多个观察者,我们需要通过遍历将所有的值分发给观察者。
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
学后检验
- 单选题 1
题目:observe()与observeForever()的主要区别是?
A.observe()只能在主线程调用,observeForever()可以在任意线程调用
B.observe()会随 LifecycleOwner 的销毁自动移除观察者,observeForever()不会
C.observe()接收所有历史数据,observeForever()只接收新数据
D.observe()只能用在 Activity,observeForever()只能用在 Service
答案:B
解析:observe(owner,…)会与owner生命周期绑定,owner销毁时自动移除该观察者;observeForever()不绑定生命周期,需要手动调用removeObserver(),否则易造成内存泄漏。 - 单选题 2
题目:LiveData 产生“黏性”事件的根本原因是?
A.postValue()内部会把上一次的值缓存下来
B. LiveData 记录了mVersion,新观察者在注册时会检查mLastVersion < mVersion
C. 只有observeForever()才会有黏性,普通observe()不会
D. LifecycleOwner 状态不在 STARTED 之上时,会缓存所有事件
答案:B
解析:LiveData 内部维护全局版本号mVersion,每次setValue()会自增;新观察者注册时用其mLastVersion与当前mVersion比对,若小于则立即分发,从而产生“黏性”行为。 - 单选题 3
题目:调用setValue()必须满足的条件是?
A. 必须在主线程,否则抛异常
B. 值不能为 null
C. 只能在postValue()的回调中使用
D. 只能在构造函数里调用
答案:A
解析:setValue()强制要求在主线程调用,若在后台线程使用会抛出 “must call on main thread” 异常。 - 单选题 4
题目:在dispatchingValue()方法中,为什么使用while (mDispatchInvalidated)循环?
A. 为了在多线程场景下保证线程安全
B. 为了防止分发过程中注册/移除观察者导致的迭代错误
C. 为了让每个观察者多次收到同一数据
D. 为了在 LifecycleOwner 暂时不可见时缓存事件
答案:B
解析:若在分发过程中有观察者增删,会将mDispatchInvalidated标记为 true,循环结束后重新分发一次,避免并发修改导致遗漏或异常。 - 单选题 5
题目:下面哪个字段代表 LiveData 当前已分发到的最新版本?
A.mActiveCount
B.mData
C.mVersion
D.mDispatchingValue
答案:C
解析:mVersion表示 LiveData 的当前版本号,每次setValue()(或最终执行的postValue())都会自增一次,作为更新时序标记。
-
多选题 6
题目:以下哪些操作会导致潜在的内存泄漏?(多选)
A. 使用observeForever()注册观察者,却忘了removeObserver()
B. 在回调中调用setValue()
C. 把 LiveData 保存在object单例中
D. 在onPause()中移除所有观察者
答案:A、C
解析:- A:
observeForever()不受生命周期绑定,若不手动removeObserver()会一直持有观察者导致泄漏。 - C:LiveData 放单例且不解绑,也会持续持有观察者。B、D 与泄漏无直接关系。
- A:
-
多选题 7
题目:在反射“去除黏性事件” 的hook()方法里,需要修改以下哪些私有字段?(多选)
A. LiveData 的mObservers
B. SafeIterableMap 中的mVersion
C. ObserverWrapper 的mLastVersion
D. LifecycleOwner 的mCurrentState
答案:A、C
解析:- 先通过反射取出 LiveData 私有的
mObservers,获取到对应的ObserverWrapper。 - 再把该
ObserverWrapper的mLastVersion设为与 LiveData 的mVersion一致,从而避免黏性。B、D 不需要改。
- 先通过反射取出 LiveData 私有的
-
多选题 8
题目:下面哪些场景会触发onActive()/onInactive()回调?(多选)
A. 第一个活跃观察者注册时
B. 最后一个活跃观察者移除时
C. LifecycleOwner 从 STARTED → RESUMED
D. LifecycleOwner 从 RESUMED → STARTED
答案:A、B
解析:- 当活跃观察者数量从 0 增加到 1 时触发
onActive(); - 当从 1 减少到 0 时触发
onInactive()。C、D 仅是生命周期内部状态变更,不改变活跃观察者计数。
- 当活跃观察者数量从 0 增加到 1 时触发
- 判断题 9
题目:LiveData 在 Activity 旋转后会自动恢复最新的值。
答案:✔
解析:Activity 重建后重新调用observe,新观察者的mLastVersion小于当前mVersion,会立即分发最新值,从而恢复 UI。 - 判断题 10
题目:postValue()不会立即通知观察者,但会在主线程执行setValue()。
答案:✔
解析:postValue()会把值放入mPendingData并安排主线程 Runnable 调用setValue(),再统一分发。 - 判断题 11
题目:如果观察者的mLastVersion >= mVersion,则该观察者不会收到本次更新。
答案:✔
解析:只有当mLastVersion < mVersion时,才会调用onChanged(),否则跳过以避免重复。 - 判断题 12
题目:调用observe()时,如果 LifecycleOwner 还没 STARTED,LiveData 会缓存该事件,等到 STARTED 后再分发。
答案:✔
解析:在非活跃状态注册时,LiveData 会在内部保留最新数据,等onActive()时才真正通过dispatchingValue()分发。 - 判断题 13
题目:MutableStateFlow在 Compose 中可以代替MutableLiveData并且默认没有黏性问题。
答案:✘
解析:MutableStateFlow同样会向新收集者发出当前值,这也是状态流的设计,并非“去黏性”。
-
简答题 14
题目:简述setValue()与postValue()的区别,以及各自的使用场景。
答案:setValue(T):必须在主线程调用;立即更新mData、mVersion++,并同步调用dispatchingValue()分发给活跃观察者。postValue(T):可在任意线程调用;将值写入mPendingData,并通过ArchTaskExecutor安排在主线程调用一次setValue()。
使用场景:UI 线程更新用setValue;在后台线程(网络、IO)更新用postValue。
-
简答题 15
题目:解释 LiveData 的内部字段mVersion、mLastVersion、mActiveCount的含义,以及它们在数据分发中的作用。
答案:mVersion:LiveData 的全局版本号,每次setValue()(或最终执行的postValue())自增,用于标记数据的更新时序。ObserverWrapper.mLastVersion:记录该观察者上次接收数据时的版本,避免重复接收相同或更旧的版本。mActiveCount:当前活跃(LifecycleOwner 至少 STARTED 且已注册)的观察者数量,从 0→1 时触发onActive(),从 1→0 时触发onInactive()。
-
简答题 16
题目:在不使用反射的前提下,如何设计或封装一个 LiveData,以避免“黏性事件”带来的重复回调?
答案示例:/** * EventLiveData:每个值只分发一次,新观察者不会收到之前的事件。 */ class EventLiveData<T> : MutableLiveData<T>() { private val handledKeys = mutableSetOf<Int>() override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { super.observe(owner) { t -> val key = System.identityHashCode(t) if (handledKeys.add(key)) { observer.onChanged(t) } } } }解析:利用对象
hashCode(或自增序号)跟踪已分发的事件,防止新注册的观察者收到旧事件。
-
编码题 17
题目:请实现一个自定义 LiveDataSingleLiveEvent<T>,保证每个值只会被观察者收到一次,不会发生黏性回调。public class ProtectedUnPeekLiveData<T> extends LiveData<T> { /** 是否允许分发 null 值 */ protected boolean isAllowNullValue; /** * 存储每个 Observer 的“已消费”状态 * key:observer 的唯一标识(这里用 identityHashCode) * value:true 表示「允许分发下一次值」,false 则「已经消费过当前值」 */ private final HashMap<Integer, Boolean> observers = new HashMap<>(); /** * 在 Activity 范围内注册一个 Observer * @param activity 生命周期所有者,通常传入宿主 Activity * @param observer 真正接收数据更新的回调 */ public void observeInActivity( @NonNull AppCompatActivity activity, @NonNull Observer<? super T> observer) { // 用 activity 自身作为 LifecycleOwner LifecycleOwner owner = activity; // 用 observer 对象的 identityHashCode 作为唯一 storeId // 保证同一个 ViewModel 环境中,不同 observer 获得不同标识 Integer storeId = System.identityHashCode(observer); observe(storeId, owner, observer); } /** * 核心 observe 实现:维护每个 Observer 的「可消费」状态 * @param storeId observer 唯一 ID * @param owner 生命周期所有者 * @param observer 真正的回调 */ private void observe( @NonNull Integer storeId, @NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { if (observers.get(storeId) == null) { // 第一次注册,初始化为 true ——“跳过一次”老数据 observers.put(storeId, true); } // 调用父类 observe,拦截数据分发 super.observe(owner, t -> { // 只有当当前 observer 的 flag 为 false 时才分发一次 // 这样确保每次 setValue 之后,每个 observer 只触发一次 onChanged if (!observers.get(storeId)) { // 设置为已消费过,后续不再重复分发同一数据 observers.put(storeId, true); // 按需过滤 null(若不允许 null 则跳过) if (t != null || isAllowNullValue) { observer.onChanged(t); } } }); } /** * 发布新数据 * @param value 不能为 null,除非 isAllowNullValue=true */ @Override protected void setValue(T value) { // 过滤不允许的 null if (value != null || isAllowNullValue) { // 每次新值到来前,先把所有 observer 的「已消费状态」重置为 false // 下一轮分发时,会被上面的 onChanged() 逻辑检测到并派送一次 for (Map.Entry<Integer, Boolean> entry : observers.entrySet()) { entry.setValue(false); } super.setValue(value); } } /** * 清空当前值(通常用于重置状态) */ protected void clear() { super.setValue(null); } } -
编码题 18
题目:在MainActivity中,使用 LiveData 监听网络请求状态,并根据状态控制 UI。// UIState 定义 sealed class UiState { object Loading : UiState() data class Success(val data: List<String>) : UiState() data class Error(val msg: String) : UiState() } // ViewModel 实现 class MainViewModel(private val repo: Repo) : ViewModel() { private val _uiState = MutableLiveData<UiState>() val uiState: LiveData<UiState> = _uiState fun loadData() { _uiState.value = UiState.Loading viewModelScope.launch { try { val list = repo.fetchItems() _uiState.value = UiState.Success(list) } catch (e: Exception) { _uiState.value = UiState.Error(e.message ?: "Unknown error") } } } } // Activity 中的观察与渲染 class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val progressBar = findViewById<ProgressBar>(R.id.progressBar) val errorText = findViewById<TextView>(R.id.errorText) val recycler = findViewById<RecyclerView>(R.id.recyclerView) val adapter = MyAdapter().also { recycler.adapter = it } viewModel.uiState.observe(this) { state -> when (state) { is UiState.Loading -> { progressBar.visibility = View.VISIBLE errorText.visibility = View.GONE } is UiState.Success -> { progressBar.visibility = View.GONE errorText.visibility = View.GONE adapter.submitList(state.data) } is UiState.Error -> { progressBar.visibility = View.GONE errorText.visibility = View.VISIBLE errorText.text = state.msg } } } // 触发数据加载 viewModel.loadData() } }