设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间01 现在时间01 缓存数据 你是我生命中所能经历的,最最深切的感觉。

你是我生命中所能经历的,最最深切的感觉。

查看: 1240|回复: 3

Android Jetpack 组件LiveData源码解析

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:236
  • 打卡月天数:0
  • 打卡总奖励:4272
  • 最近打卡:2025-03-29 13:17:14
等级头衔

等級:晓枫资讯-上等兵

在线时间
6 小时

积分成就
威望
0
贡献
438
主题
414
精华
0
金钱
5650
积分
932
注册时间
2023-1-6
最后登录
2025-3-29

发表于 2023-3-11 22:33:53 | 显示全部楼层 |阅读模式
前言

本文来分析下 LiveData 的源码,以及其在实际开发中的一些问题。

基本使用

一般来说 LiveData 都会配合 ViewModel 使用,篇幅原因关于 ViewModel 的内容将在后续博客中分析,目前可以将 ViewModel 理解为一个生命周期比 Activity 更长的对象,且不会造成内存泄漏。
示例代码:
MainViewModel.kt
  1. class MainViewModel: ViewModel() {
  2.     // 定义 LiveData 注意这里给了 0 作为初始值
  3.     val number = MutableLiveData<Int>(0)
  4.     fun add(){
  5.         // 相当于 number.setValue(number.getValue() + 1)
  6.         number.value = number.value?.plus(1)
  7.     }
  8.     fun sub(){
  9.         // 相当于 number.setValue(number.getValue() - 1)
  10.         number.value = number.value?.minus(1)
  11.     }
  12. }
复制代码
MainACtivity.kt
  1. class MainActivity : AppCompatActivity() {
  2.     override fun onCreate(savedInstanceState: Bundle?) {
  3.         super.onCreate(savedInstanceState)
  4.         setContentView(R.layout.activity_main)
  5.         // 获取 ViewModel 实例
  6.         val vm = ViewModelProvider(this).get(MainViewModel::class.java)
  7.         // 调用 ViewModel 方法 进行加法操作
  8.         bnAdd.setOnClickListener {
  9.              vm.add()
  10.         }
  11.         // 调用 ViewModel 方法 进行减法操作
  12.         bnSub.setOnClickListener {
  13.             vm.sub()
  14.         }
  15.         // 观察 LiveData 变化, Observer 是接口,kotlin 写法简化
  16.         vm.number.observe(this, Observer {
  17.             tvNumber.text = it.toString()
  18.         })
  19.     }
  20. }
复制代码
XML 非常简单就不贴了,看下效果图:
233518crdvmnbmn7aramnj.webp


疑问

很简单的功能,但是有两个问题需要注意:

  • 在 XML 中并没有给中间的 TextView 设置 text 属性,仅仅给 LiveData 赋值了初始值 0,就可以直接显示到 TextView 上;
  • 数值发生变化后,进行横竖屏切换后 TextView 依然保持着最新值(如果 number 作为普通 Int 放在 Activity 中,当 Activity 由于横竖屏切换导致重建会重新变为 0);
本文将以这两个问题作为切入点,对 LiveData 源码进行分析。

源码分析


Observer

从实例代码中很容易看出这是典型的观察者模式,当 LiveData 发生变化时会对其订阅者发送通知,将最新值传递过去,Observer 就相当于其观察者,先来看一下 Observer 接口:
  1. public interface Observer<T> {
  2.     void onChanged(T t);
  3. }
复制代码
当 LiveData 发生变化时,就会触发其观察者的 onChanged 方法,并传递最新值;
再看一下其添加订阅时的源码:
  1. public abstract class LiveData<T> {
  2.     //...
  3.     public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
  4.         // 检查是否在主线程
  5.         assertMainThread("observe");
  6.         // 如果观察者所在组件的生命周期为 DESTROYED 则直接 return
  7.         if (owner.getLifecycle().getCurrentState() == DESTROYED) {
  8.             return;
  9.         }
  10.         // LifecycleBoundObserver 实现了 ObserverWrapper
  11.         // 理解为这是对 观察者 Observer 的一层包装类即可
  12.         LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
  13.         // mObservers 是一个 Map 容器,原始的 Observer 为 key,包装后的 wrapper 为 value
  14.         ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
  15.         // 同一个 observer 不能在不同的生命周期组件中进行订阅
  16.         if (existing != null && !existing.isAttachedTo(owner)) {
  17.             throw new IllegalArgumentException("Cannot add the same observer"
  18.                     + " with different lifecycles");
  19.         }
  20.         // 重复订阅直接return
  21.         if (existing != null) {
  22.             return;
  23.         }
  24.         // LifecycleBoundObserver 利用 Lifecycle 实现自动解绑
  25.         // Lifecycle 原理详见我之前的博客
  26.         owner.getLifecycle().addObserver(wrapper);
  27.     }
  28.     // ...
  29. }
复制代码
从源码中得知订阅必须在主线程(这一点也非常适用于 Android 的 UI 更新), 订阅后会放入一个 Map 容器中存储;

ObserverWrapper

接着来看一下 LiveData 是如何对 Observer 进行包装的,LifecycleBoundObserver 实现了 ObserverWrapper,那么就先来看看 ObserverWrapper 的源码:
  1. private abstract class ObserverWrapper {
  2.     final Observer<? super T> mObserver; // Observer 原始对象
  3.     boolean mActive; // 是否激活
  4.     int mLastVersion = START_VERSION; // 版本号 默认 -1
  5.     ObserverWrapper(Observer<? super T> observer) {
  6.         mObserver = observer; // 赋值
  7.     }
  8.     abstract boolean shouldBeActive(); // 抽象方法
  9.     boolean isAttachedTo(LifecycleOwner owner) {
  10.         return false;
  11.     }
  12.     void detachObserver() {
  13.     }
  14.     void activeStateChanged(boolean newActive) {
  15.         if (newActive == mActive) { // 如果值一样则返回
  16.             return;
  17.         }
  18.         mActive = newActive; // 不一样则更新 mActive
  19.         changeActiveCounter(mActive ? 1 : -1); // 记录有多少个激活状态的observer
  20.         // 注意这里,如果mActive是从false变更为true 则调用一次 dispatchingValue
  21.         // dispatchingValue 的源码下面再分析
  22.         if (mActive) {
  23.             dispatchingValue(this);
  24.         }
  25.     }
  26. }
复制代码
LifecycleBoundObserver

接着看一下 LifecycleBoundObserver 的源码:
  1. class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
  2.     @NonNull
  3.     final LifecycleOwner mOwner;
  4.     LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
  5.         super(observer); // 父类构造器 赋值
  6.         mOwner = owner;
  7.     }
  8.     @Override
  9.     boolean shouldBeActive() { // 判断是否是激活状态
  10.         return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
  11.     }
  12.     // 如果再 activity 中进行 observer
  13.     // 当 activity 生命周期发生变化时 会回调到这里
  14.     @Override
  15.     public void onStateChanged(@NonNull LifecycleOwner source,
  16.             @NonNull Lifecycle.Event event) {
  17.         Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
  18.         // 自动解绑
  19.         if (currentState == DESTROYED) {
  20.             // removeObserver 内部会将 observer 从 map 容器中移除
  21.             // 并且调用其 detachObserver 方法
  22.             removeObserver(mObserver);
  23.             return;
  24.         }
  25.         Lifecycle.State prevState = null;
  26.         while (prevState != currentState) {
  27.             prevState = currentState;
  28.             // activeStateChanged 上面已经说过了
  29.             // 如果 mActive 由 fasle 变更为 true 会执行一次 dispatchingValue
  30.             activeStateChanged(shouldBeActive());
  31.             currentState = mOwner.getLifecycle().getCurrentState();
  32.         }
  33.     }
  34.     // ...
  35. }
复制代码
MutableLiveData

上述的观察者相关的重要源码已经分析完,接着来看一下示例代码中定义的 MutableLiveData 源码:
  1. public class MutableLiveData<T> extends LiveData<T> {
  2.     public MutableLiveData(T value) {
  3.         super(value);
  4.     }
  5.     public MutableLiveData() {
  6.         super();
  7.     }
  8.     @Override
  9.     public void postValue(T value) {
  10.         super.postValue(value);
  11.     }
  12.     @Override
  13.     public void setValue(T value) {
  14.         super.setValue(value);
  15.     }
  16. }
复制代码
继承自 LiveData,作用很明显暴露出其 postValue、setValue 方法,那么就先来看一下这两个方法调用逻辑

postValue

先来看看 postValue:
  1. public abstract class LiveData<T> {
  2.     protected void postValue(T value) {
  3.         boolean postTask;
  4.         synchronized (mDataLock) {
  5.             // mPendingData 默认值为 NOT_SET
  6.             postTask = mPendingData == NOT_SET;
  7.             // 调用 postValue 后,会赋值成传进来的 value
  8.             mPendingData = value;
  9.         }
  10.         if (!postTask) { // 第一次调用 肯定为 true
  11.             return;
  12.         }
  13.         // 核心在于这一行,postToMainThread
  14.         // 看名字也知道是切换到主线程去执行 mPostValueRunnable
  15.         ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
  16.     }
  17. }
复制代码
ArchTaskExecutor.getInstance() 会初始化其内部的 mDelegate 变量,其最终实现是 DefaultTaskExecutor;DefaultTaskExecutor 内部包含一个主线程 Handler,其 postToMainThread 方法就是利用 Handler 将 runnable 发送至主线程执行。这里面的源码比较简单,就不贴出来细节了,看一下 mPostValueRunnable 具体执行了什么:
  1. private final Runnable mPostValueRunnable = new Runnable() {
  2.     @Override
  3.     public void run() {
  4.         Object newValue;
  5.         synchronized (mDataLock) { // 加锁同步
  6.             newValue = mPendingData; // 获取最新传递过来的值
  7.             mPendingData = NOT_SET; // 将 mPendingData 恢复为默认值
  8.         }
  9.         // 最终还是调用了 setValue
  10.         setValue((T) newValue);
  11.     }
  12. };
复制代码
可以看出 postValue 可以在任意线程调用,最终都会被切换到主线程调用 setValue,但是需要注意,频繁调用 postValue 可能会只保留最后一次的值,因为每次 postValue 会导致 mPendingData 设置为新的值,但如果多次 postValue 在子线程执行,但是主线程还没有来得及执行 mPostValueRunnable,会导致 mPendingData 没有被恢复为 NOT_SET,那么 postTask 即为 false,但 mPendingData 会设置为最新值,当 mPostValueRunnable 执行时从 mPendingData 中获取的也是最新值。

setValue

postValue 内部最终调用了 setValue,那么就来看看 setValue 的源码:
  1. public abstract class LiveData<T> {
  2.     static final int START_VERSION = -1;
  3.     private volatile Object mData;
  4.     private int mVersion
  5.     // 带初始值的构造器
  6.     public LiveData(T value) {
  7.         mData = value; // 直接给 mData 赋值
  8.         mVersion = START_VERSION + 1; //版本号 +1,也就是 0
  9.     }
  10.     // 无参构造器
  11.     public LiveData() {
  12.         mData = NOT_SET;
  13.         mVersion = START_VERSION; // 版本号默认 -1
  14.     }
  15.     protected void setValue(T value) {
  16.         // 内部根据 Looper 判断是否在主线程,不在主线程则抛出异常
  17.         assertMainThread("setValue");
  18.         // 版本号 +1
  19.         mVersion++;
  20.         // LiveData 的数据,也就是被观察的数据,设置为最新值
  21.         mData = value;
  22.         // 这里是重点
  23.         dispatchingValue(null);
  24.     }
  25. }
复制代码
从源码中得知,setValue 只能从主线程调用,内部对版本号进行++操作,并且设置 mData 为最新值,最终调用 dispatchingValue:
  1. // 用于保存其观察者 Observer,Observer 会包装成
  2. private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
  3.         new SafeIterableMap<>();
  4. void dispatchingValue(@Nullable ObserverWrapper initiator) {
  5.     if (mDispatchingValue) { // 默认为 false
  6.         mDispatchInvalidated = true;
  7.         return;
  8.     }
  9.     mDispatchingValue = true; // 进入方法后设置为 true
  10.     do {
  11.         mDispatchInvalidated = false;
  12.         // setValue 传进来的是 null 不会进入这个 if
  13.         // initiator 实际上就是观察者,如果传递进来一个观察者对象
  14.         // 则只进行一次 considerNotify 方法调用
  15.         if (initiator != null) {
  16.             considerNotify(initiator);
  17.             initiator = null;
  18.         } else { // 遍历自身的观察者
  19.             for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
  20.                     mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
  21.                 // 调用 considerNotify 将观察者传入
  22.                 considerNotify(iterator.next().getValue());
  23.                 if (mDispatchInvalidated) {
  24.                     break;
  25.                 }
  26.             }
  27.         }
  28.     } while (mDispatchInvalidated);
  29.     mDispatchingValue = false; // 方法执行结束前 设置为 false
  30. }
  31. private void considerNotify(ObserverWrapper observer) {
  32.     if (!observer.mActive) { // 未激活状态直接返回
  33.         return;
  34.     }
  35.     // 判断是否可以是激活状态
  36.     // LifecycleBoundObserver 中则是判断所在组件的生命周期是否为激活状态
  37.     if (!observer.shouldBeActive()) {
  38.         observer.activeStateChanged(false); // 将 observer 的 mActive 设置为 fasle
  39.         return;
  40.     }
  41.     // 如果 observer 的版本号 大于 LiveData 本身的版本号 则直接返回
  42.     if (observer.mLastVersion >= mVersion) {
  43.         return;
  44.     }
  45.     // 将 observer 的版本号和 LiveData 本身的版本号同步
  46.     observer.mLastVersion = mVersion;
  47.     // 触发其 onChanged 方法回调
  48.     observer.mObserver.onChanged((T) mData);
  49. }
复制代码
setValue 的源码并不复杂,总结一下:

  • mVersion 版本号 ++ 操作,并且 mData 设置为最新数据;
  • dispatchingValue(null) 遍历观察者容器,对符合条件的观察者调用其 onChanged 方法回调。

问题答疑

从源码中我们可以了解到,当调用 LiveData.observer 时,我们传入的 observer 对象会被包装成为 LifecycleBoundObserver,会自动感知所在组件的生命周期;
又因为 Lifecycle 会在观察组件生命周期之后就会进行状态同步,所以我们再调用 LiveData.observer 之后会触发一次 activeStateChanged,导致 observer 的 mActive 由 fasle 变为 true,所以会进行一次 dispatchingValue;
在示例代码中我们给 MainViewModel 中的 number 赋值了初始值 0,那么初始化时会调用 LiveData 有参的构造函数,其中对 mVersion 进行了 +1 操作,此时的 LiveData 中的 mVersion 变为了 0,而 observer 中的 mLastVersion 为 -1,所以会进行一次分发,所以 TextView 的 text 被设置为了 0;
而第二个问题和上述的原因类似,不过特殊点在于 number 是被定义在在 ViewModel 中,开头也提到过 ViewModel 暂时可以理解为生命周期长于 Activity 的对象,那么当 Activity 由于横竖屏切换导致重建后, ViewModel 中的数据并没有清楚,LiveData 自然保持着他的 mData 最新值以及其 mVersion 版本号,当 Actvitiy 重新调用 LiveData.observer 进行订阅时,传入的 observer 的 mVersion 已经变为 -1,所以同样会触发一次 onChanged 回调得到最新值;

LiveData 特性引出的问题

上述问题答疑中其实可以看出 LiveData 订阅后可以获取最新值这在数据流中属于粘性 事件。在示例代码中,横竖屏切换后仍然可以获取最新的值,这比较符合用户使用习惯。但实际开发中往往有着更复杂的场景,比如:定义一个 LiveData<Boolean>(false) 表示是否需要展示加载中弹窗,假设需求是用户点击按钮后展示,此时用户点击按钮,将其设置为 true,那么此时 Activiy 发生重建导致生命周期重新走一遍,此时的 LiveData 的 value 仍然为 true,重建后用户并没有点击按钮但弹窗仍然会显示;
这是一个很常见的业务需求,发生这种问题的根本原因是生命周期重新走之后导致 observer 的 mLastVersion 变更为 -1,而 LiveData 的 mVersion 不变,导致重新触发 onChanged 方法回调;
遇到这种情况该怎么办呢?难道 LiveData 设计的有问题?我认为这并非 google 官方设计的不好,而是 LiveData 本身就应该作用于时时刻刻需要获取最新值的场景,而并非所有的数据都需要放到 ViewModel 中用 LiveData 包裹。上述的问题更多的我认为是 LiveData 滥用而导致的。 但 LiveData 的 onChanged 的数据变化后进行回调很多场景使用起来又很方便,该怎么办?

问题解决

既然已经知道原因,源码又了解的差不多,很容易就能找到问题的切入点;那就是 considerNotify 方法中会有层层判断,只要有一个不符合则不会触发 onChanged 方法回调,可以反射修改 observer 的 mLastVersion 使其重新订阅后仍然和 LiveData 保持一致。 不过利用到了反射,那么风险度也自然提高。
还有更好的办法,SingleLiveData!我最初看到这个类是在 github 中的一个 issue 中,后来网上流传了很多版本,其原理是对 LiveData 进行包装,内部定义一个 HashMap<Observer<in T>, AtomicBoolean> 容器,重写其 observer 订阅方法,每个 observer 对应一个 AtomicBoolean 对象,在 setValue 之前先遍历将所有的 AtomicBoolean 设置为 true,接着重写其 observer 包装一层,在分发时判断并修改 AtomicBoolean 为 false。
我觉得这也是比较好的规避问题的方法,这里就随便贴一个了:
  1. class SingleLiveData<T> : MutableLiveData<T>() {
  2.     private val mPendingMap = HashMap<Observer<in T>, AtomicBoolean>()
  3.     @MainThread
  4.     override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
  5.         val lifecycle = owner.lifecycle
  6.         if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
  7.             return
  8.         }
  9.         mPendingMap[observer] = AtomicBoolean(false)
  10.         lifecycle.addObserver(LifecycleEventObserver { source: LifecycleOwner?, event: Lifecycle.Event ->
  11.             if (event == Lifecycle.Event.ON_DESTROY) {
  12.                 mPendingMap.remove(observer)
  13.             }
  14.         })
  15.         super.observe(owner) { t: T ->
  16.             val pending = mPendingMap[observer]
  17.             if (pending != null && pending.compareAndSet(true, false)) {
  18.                 observer.onChanged(t)
  19.             }
  20.         }
  21.     }
  22.     @MainThread
  23.     override fun observeForever(observer: Observer<in T>) {
  24.         mPendingMap[observer] = AtomicBoolean(false)
  25.         super.observeForever(observer)
  26.     }
  27.     @MainThread
  28.     override fun removeObserver(observer: Observer<in T>) {
  29.         mPendingMap.remove(observer)
  30.         super.removeObserver(observer)
  31.     }
  32.     @MainThread
  33.     override fun removeObservers(owner: LifecycleOwner) {
  34.         mPendingMap.clear()
  35.         super.removeObservers(owner)
  36.     }
  37.     @MainThread
  38.     override fun setValue(t: T?) {
  39.         for (value in mPendingMap.values) {
  40.             value.set(true)
  41.         }
  42.         super.setValue(t)
  43.     }
  44. }
复制代码
最后

我对于 LiveData 和网络上认为需要用 Flow 替换 LiveData 的观点不同,我觉得 LiveData 和 Flow 其实应该共存,或者说是结合实际场景具体选择,在需要绑定生命周期的场景下 LiveData 就是最佳选择,没必要强行使用 Flow,虽然 Flow 也提供了关联生命周期的做法,但如果项目中已经大面积使用 LiveData 真的没必要强行去替换,尤其是 Java Kotlin 结合的项目,Java 不支持 Flow 的情况下使用 LiveData 是最佳的选择;
以上就是Android Jetpack 组件LiveData源码解析的详细内容,更多关于Android Jetpack LiveData的资料请关注晓枫资讯其它相关文章!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:20
  • 打卡月天数:1
  • 打卡总奖励:276
  • 最近打卡:2025-07-03 12:03:09
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
319
积分
42
注册时间
2023-4-11
最后登录
2025-7-3

发表于 2023-4-14 02:45:18 | 显示全部楼层
谢谢分享~~~~~
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
18
积分
16
注册时间
2022-12-26
最后登录
2022-12-26

发表于 2024-9-24 11:29:38 | 显示全部楼层
顶顶更健康!!!
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
15
积分
10
注册时间
2022-12-29
最后登录
2022-12-29

发表于 2025-2-28 11:26:50 | 显示全部楼层
感谢楼主,顶。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼
4楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表