LiveData的基本使用与原理解析

588 阅读41分钟

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;
}

测试源码:github.com/xingchaozha…

学后检验

  1. 单选题 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. 单选题 2
    题目:LiveData 产生“黏性”事件的根本原因是?
    A. postValue() 内部会把上一次的值缓存下来
    B. LiveData 记录了 mVersion,新观察者在注册时会检查 mLastVersion < mVersion
    C. 只有 observeForever() 才会有黏性,普通 observe() 不会
    D. LifecycleOwner 状态不在 STARTED 之上时,会缓存所有事件
    答案:B
    解析:LiveData 内部维护全局版本号 mVersion,每次 setValue() 会自增;新观察者注册时用其 mLastVersion 与当前 mVersion 比对,若小于则立即分发,从而产生“黏性”行为。
  3. 单选题 3
    题目:调用 setValue() 必须满足的条件是?
    A. 必须在主线程,否则抛异常
    B. 值不能为 null
    C. 只能在 postValue() 的回调中使用
    D. 只能在构造函数里调用
    答案:A
    解析setValue() 强制要求在主线程调用,若在后台线程使用会抛出 “must call on main thread” 异常。
  4. 单选题 4
    题目:在 dispatchingValue() 方法中,为什么使用 while (mDispatchInvalidated) 循环?
    A. 为了在多线程场景下保证线程安全
    B. 为了防止分发过程中注册/移除观察者导致的迭代错误
    C. 为了让每个观察者多次收到同一数据
    D. 为了在 LifecycleOwner 暂时不可见时缓存事件
    答案:B
    解析:若在分发过程中有观察者增删,会将 mDispatchInvalidated 标记为 true,循环结束后重新分发一次,避免并发修改导致遗漏或异常。
  5. 单选题 5
    题目:下面哪个字段代表 LiveData 当前已分发到的最新版本?
    A. mActiveCount
    B. mData
    C. mVersion
    D. mDispatchingValue
    答案:C
    解析mVersion 表示 LiveData 的当前版本号,每次 setValue()(或最终执行的 postValue())都会自增一次,作为更新时序标记。

  1. 多选题 6
    题目:以下哪些操作会导致潜在的内存泄漏?(多选)
    A. 使用 observeForever() 注册观察者,却忘了 removeObserver()
    B. 在回调中调用 setValue()
    C. 把 LiveData 保存在 object 单例中
    D. 在 onPause() 中移除所有观察者
    答案:A、C
    解析

    • A:observeForever() 不受生命周期绑定,若不手动 removeObserver() 会一直持有观察者导致泄漏。
    • C:LiveData 放单例且不解绑,也会持续持有观察者。B、D 与泄漏无直接关系。
  2. 多选题 7
    题目:在反射“去除黏性事件” 的 hook() 方法里,需要修改以下哪些私有字段?(多选)
    A. LiveData 的 mObservers
    B. SafeIterableMap 中的 mVersion
    C. ObserverWrapper 的 mLastVersion
    D. LifecycleOwner 的 mCurrentState
    答案:A、C
    解析

    • 先通过反射取出 LiveData 私有的 mObservers,获取到对应的 ObserverWrapper
    • 再把该 ObserverWrappermLastVersion 设为与 LiveData 的 mVersion 一致,从而避免黏性。B、D 不需要改。
  3. 多选题 8
    题目:下面哪些场景会触发 onActive() / onInactive() 回调?(多选)
    A. 第一个活跃观察者注册时
    B. 最后一个活跃观察者移除时
    C. LifecycleOwner 从 STARTED → RESUMED
    D. LifecycleOwner 从 RESUMED → STARTED
    答案:A、B
    解析

    • 当活跃观察者数量从 0 增加到 1 时触发 onActive()
    • 当从 1 减少到 0 时触发 onInactive()。C、D 仅是生命周期内部状态变更,不改变活跃观察者计数。

  1. 判断题 9
    题目:LiveData 在 Activity 旋转后会自动恢复最新的值。
    答案:✔
    解析:Activity 重建后重新调用 observe,新观察者的 mLastVersion 小于当前 mVersion,会立即分发最新值,从而恢复 UI。
  2. 判断题 10
    题目postValue() 不会立即通知观察者,但会在主线程执行 setValue()
    答案:✔
    解析postValue() 会把值放入 mPendingData 并安排主线程 Runnable 调用 setValue(),再统一分发。
  3. 判断题 11
    题目:如果观察者的 mLastVersion >= mVersion,则该观察者不会收到本次更新。
    答案:✔
    解析:只有当 mLastVersion < mVersion 时,才会调用 onChanged(),否则跳过以避免重复。
  4. 判断题 12
    题目:调用 observe() 时,如果 LifecycleOwner 还没 STARTED,LiveData 会缓存该事件,等到 STARTED 后再分发。
    答案:✔
    解析:在非活跃状态注册时,LiveData 会在内部保留最新数据,等 onActive() 时才真正通过 dispatchingValue() 分发。
  5. 判断题 13
    题目MutableStateFlow 在 Compose 中可以代替 MutableLiveData 并且默认没有黏性问题。
    答案:✘
    解析MutableStateFlow 同样会向新收集者发出当前值,这也是状态流的设计,并非“去黏性”。

  1. 简答题 14
    题目:简述 setValue()postValue() 的区别,以及各自的使用场景。
    答案

    • setValue(T):必须在主线程调用;立即更新 mDatamVersion++,并同步调用 dispatchingValue() 分发给活跃观察者。
    • postValue(T):可在任意线程调用;将值写入 mPendingData,并通过 ArchTaskExecutor 安排在主线程调用一次 setValue()
      使用场景:UI 线程更新用 setValue;在后台线程(网络、IO)更新用 postValue
  2. 简答题 15
    题目:解释 LiveData 的内部字段 mVersionmLastVersionmActiveCount 的含义,以及它们在数据分发中的作用。
    答案

    • mVersion:LiveData 的全局版本号,每次 setValue()(或最终执行的 postValue())自增,用于标记数据的更新时序。
    • ObserverWrapper.mLastVersion:记录该观察者上次接收数据时的版本,避免重复接收相同或更旧的版本。
    • mActiveCount:当前活跃(LifecycleOwner 至少 STARTED 且已注册)的观察者数量,从 0→1 时触发 onActive(),从 1→0 时触发 onInactive()
  3. 简答题 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(或自增序号)跟踪已分发的事件,防止新注册的观察者收到旧事件。


  1. 编码题 17
    题目:请实现一个自定义 LiveData SingleLiveEvent<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);
        }
    }
    
  2. 编码题 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()
      }
    }