时间:2023-03-11来源:系统城装机大师作者:佚名
一般来说 LiveData 都会配合 ViewModel 使用,篇幅原因关于 ViewModel 的内容将在后续博客中分析,目前可以将 ViewModel 理解为一个生命周期比 Activity 更长的对象,且不会造成内存泄漏。
示例代码:
MainViewModel.kt
1 2 3 4 5 6 7 8 9 10 11 12 |
class MainViewModel: ViewModel() { // 定义 LiveData 注意这里给了 0 作为初始值 val number = MutableLiveData<Int>( 0 ) fun add(){ // 相当于 number.setValue(number.getValue() + 1) number.value = number.value?.plus( 1 ) } fun sub(){ // 相当于 number.setValue(number.getValue() - 1) number.value = number.value?.minus( 1 ) } } |
MainACtivity.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 获取 ViewModel 实例 val vm = ViewModelProvider( this ).get(MainViewModel:: class .java) // 调用 ViewModel 方法 进行加法操作 bnAdd.setOnClickListener { vm.add() } // 调用 ViewModel 方法 进行减法操作 bnSub.setOnClickListener { vm.sub() } // 观察 LiveData 变化, Observer 是接口,kotlin 写法简化 vm.number.observe( this , Observer { tvNumber.text = it.toString() }) } } |
XML 非常简单就不贴了,看下效果图:
很简单的功能,但是有两个问题需要注意:
本文将以这两个问题作为切入点,对 LiveData 源码进行分析。
从实例代码中很容易看出这是典型的观察者模式,当 LiveData 发生变化时会对其订阅者发送通知,将最新值传递过去,Observer 就相当于其观察者,先来看一下 Observer 接口:
1 2 3 |
public interface Observer<T> { void onChanged(T t); } |
当 LiveData 发生变化时,就会触发其观察者的 onChanged 方法,并传递最新值;
再看一下其添加订阅时的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public abstract class LiveData<T> { //... public void observe( @NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { // 检查是否在主线程 assertMainThread( "observe" ); // 如果观察者所在组件的生命周期为 DESTROYED 则直接 return if (owner.getLifecycle().getCurrentState() == DESTROYED) { return ; } // LifecycleBoundObserver 实现了 ObserverWrapper // 理解为这是对 观察者 Observer 的一层包装类即可 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); // mObservers 是一个 Map 容器,原始的 Observer 为 key,包装后的 wrapper 为 value ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); // 同一个 observer 不能在不同的生命周期组件中进行订阅 if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException( "Cannot add the same observer" + " with different lifecycles" ); } // 重复订阅直接return if (existing != null ) { return ; } // LifecycleBoundObserver 利用 Lifecycle 实现自动解绑 // Lifecycle 原理详见我之前的博客 owner.getLifecycle().addObserver(wrapper); } // ... } |
从源码中得知订阅必须在主线程(这一点也非常适用于 Android 的 UI 更新), 订阅后会放入一个 Map 容器中存储;
接着来看一下 LiveData 是如何对 Observer 进行包装的,LifecycleBoundObserver 实现了 ObserverWrapper,那么就先来看看 ObserverWrapper 的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
private abstract class ObserverWrapper { final Observer<? super T> mObserver; // Observer 原始对象 boolean mActive; // 是否激活 int mLastVersion = START_VERSION; // 版本号 默认 -1 ObserverWrapper(Observer<? super T> observer) { mObserver = observer; // 赋值 } abstract boolean shouldBeActive(); // 抽象方法 boolean isAttachedTo(LifecycleOwner owner) { return false ; } void detachObserver() { } void activeStateChanged( boolean newActive) { if (newActive == mActive) { // 如果值一样则返回 return ; } mActive = newActive; // 不一样则更新 mActive changeActiveCounter(mActive ? 1 : - 1 ); // 记录有多少个激活状态的observer // 注意这里,如果mActive是从false变更为true 则调用一次 dispatchingValue // dispatchingValue 的源码下面再分析 if (mActive) { dispatchingValue( this ); } } } |
接着看一下 LifecycleBoundObserver 的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver { @NonNull final LifecycleOwner mOwner; LifecycleBoundObserver( @NonNull LifecycleOwner owner, Observer<? super T> observer) { super (observer); // 父类构造器 赋值 mOwner = owner; } @Override boolean shouldBeActive() { // 判断是否是激活状态 return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } // 如果再 activity 中进行 observer // 当 activity 生命周期发生变化时 会回调到这里 @Override public void onStateChanged( @NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState(); // 自动解绑 if (currentState == DESTROYED) { // removeObserver 内部会将 observer 从 map 容器中移除 // 并且调用其 detachObserver 方法 removeObserver(mObserver); return ; } Lifecycle.State prevState = null ; while (prevState != currentState) { prevState = currentState; // activeStateChanged 上面已经说过了 // 如果 mActive 由 fasle 变更为 true 会执行一次 dispatchingValue activeStateChanged(shouldBeActive()); currentState = mOwner.getLifecycle().getCurrentState(); } } // ... } |
上述的观察者相关的重要源码已经分析完,接着来看一下示例代码中定义的 MutableLiveData 源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class MutableLiveData<T> extends LiveData<T> { public MutableLiveData(T value) { super (value); } public MutableLiveData() { super (); } @Override public void postValue(T value) { super .postValue(value); } @Override public void setValue(T value) { super .setValue(value); } } |
继承自 LiveData,作用很明显暴露出其 postValue、setValue 方法,那么就先来看一下这两个方法调用逻辑
先来看看 postValue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public abstract class LiveData<T> { protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { // mPendingData 默认值为 NOT_SET postTask = mPendingData == NOT_SET; // 调用 postValue 后,会赋值成传进来的 value mPendingData = value; } if (!postTask) { // 第一次调用 肯定为 true return ; } // 核心在于这一行,postToMainThread // 看名字也知道是切换到主线程去执行 mPostValueRunnable ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); } } |
ArchTaskExecutor.getInstance() 会初始化其内部的 mDelegate 变量,其最终实现是 DefaultTaskExecutor;DefaultTaskExecutor 内部包含一个主线程 Handler,其 postToMainThread 方法就是利用 Handler 将 runnable 发送至主线程执行。这里面的源码比较简单,就不贴出来细节了,看一下 mPostValueRunnable 具体执行了什么:
1 2 3 4 5 6 7 8 9 10 11 12 |
private final Runnable mPostValueRunnable = new Runnable() { @Override public void run() { Object newValue; synchronized (mDataLock) { // 加锁同步 newValue = mPendingData; // 获取最新传递过来的值 mPendingData = NOT_SET; // 将 mPendingData 恢复为默认值 } // 最终还是调用了 setValue setValue((T) newValue); } }; |
可以看出 postValue 可以在任意线程调用,最终都会被切换到主线程调用 setValue,但是需要注意,频繁调用 postValue 可能会只保留最后一次的值,因为每次 postValue 会导致 mPendingData 设置为新的值,但如果多次 postValue 在子线程执行,但是主线程还没有来得及执行 mPostValueRunnable,会导致 mPendingData 没有被恢复为 NOT_SET,那么 postTask 即为 false,但 mPendingData 会设置为最新值,当 mPostValueRunnable 执行时从 mPendingData 中获取的也是最新值。
postValue 内部最终调用了 setValue,那么就来看看 setValue 的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public abstract class LiveData<T> { static final int START_VERSION = - 1 ; private volatile Object mData; private int mVersion // 带初始值的构造器 public LiveData(T value) { mData = value; // 直接给 mData 赋值 mVersion = START_VERSION + 1 ; //版本号 +1,也就是 0 } // 无参构造器 public LiveData() { mData = NOT_SET; mVersion = START_VERSION; // 版本号默认 -1 } protected void setValue(T value) { // 内部根据 Looper 判断是否在主线程,不在主线程则抛出异常 assertMainThread( "setValue" ); // 版本号 +1 mVersion++; // LiveData 的数据,也就是被观察的数据,设置为最新值 mData = value; // 这里是重点 dispatchingValue( null ); } } |
从源码中得知,setValue 只能从主线程调用,内部对版本号进行++操作,并且设置 mData 为最新值,最终调用 dispatchingValue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// 用于保存其观察者 Observer,Observer 会包装成 private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>(); void dispatchingValue( @Nullable ObserverWrapper initiator) { if (mDispatchingValue) { // 默认为 false mDispatchInvalidated = true ; return ; } mDispatchingValue = true ; // 进入方法后设置为 true do { mDispatchInvalidated = false ; // setValue 传进来的是 null 不会进入这个 if // initiator 实际上就是观察者,如果传递进来一个观察者对象 // 则只进行一次 considerNotify 方法调用 if (initiator != null ) { considerNotify(initiator); initiator = null ; } else { // 遍历自身的观察者 for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { // 调用 considerNotify 将观察者传入 considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break ; } } } } while (mDispatchInvalidated); mDispatchingValue = false ; // 方法执行结束前 设置为 false } private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { // 未激活状态直接返回 return ; } // 判断是否可以是激活状态 // LifecycleBoundObserver 中则是判断所在组件的生命周期是否为激活状态 if (!observer.shouldBeActive()) { observer.activeStateChanged( false ); // 将 observer 的 mActive 设置为 fasle return ; } // 如果 observer 的版本号 大于 LiveData 本身的版本号 则直接返回 if (observer.mLastVersion >= mVersion) { return ; } // 将 observer 的版本号和 LiveData 本身的版本号同步 observer.mLastVersion = mVersion; // 触发其 onChanged 方法回调 observer.mObserver.onChanged((T) mData); } |
setValue 的源码并不复杂,总结一下:
从源码中我们可以了解到,当调用 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<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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
class SingleLiveData<T> : MutableLiveData<T>() { private val mPendingMap = HashMap<Observer<in T>, AtomicBoolean>() @MainThread override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { val lifecycle = owner.lifecycle if (lifecycle.currentState == Lifecycle.State.DESTROYED) { return } mPendingMap[observer] = AtomicBoolean( false ) lifecycle.addObserver(LifecycleEventObserver { source: LifecycleOwner?, event: Lifecycle.Event -> if (event == Lifecycle.Event.ON_DESTROY) { mPendingMap.remove(observer) } }) super .observe(owner) { t: T -> val pending = mPendingMap[observer] if (pending != null && pending.compareAndSet( true , false )) { observer.onChanged(t) } } } @MainThread override fun observeForever(observer: Observer<in T>) { mPendingMap[observer] = AtomicBoolean( false ) super .observeForever(observer) } @MainThread override fun removeObserver(observer: Observer<in T>) { mPendingMap.remove(observer) super .removeObserver(observer) } @MainThread override fun removeObservers(owner: LifecycleOwner) { mPendingMap.clear() super .removeObservers(owner) } @MainThread override fun setValue(t: T?) { for (value in mPendingMap.values) { value.set( true ) } super .setValue(t) } } |
我对于 LiveData 和网络上认为需要用 Flow 替换 LiveData 的观点不同,我觉得 LiveData 和 Flow 其实应该共存,或者说是结合实际场景具体选择,在需要绑定生命周期的场景下 LiveData 就是最佳选择,没必要强行使用 Flow,虽然 Flow 也提供了关联生命周期的做法,但如果项目中已经大面积使用 LiveData 真的没必要强行去替换,尤其是 Java Kotlin 结合的项目,Java 不支持 Flow 的情况下使用 LiveData 是最佳的选择;
2024-07-07
Java框架如何实现非阻塞式编程?2023-03-11
hbuilderx设置Firefox浏览器安装路径教程 hbuilderx怎么设置Firefox浏览器安装路径?2023-03-09
C++ RBTree红黑树的性质与实现一、AVL树的概念 二、AVL树节点的定义 三、AVL树的插入 四、AVL树的旋转 1.左单旋 2.右单旋 3.左右双旋 4.右左双旋 五、进行验证 六、AVLTree的性能...
2023-03-09
1、idea构建web项目 1、新建一个空项目 2、新建java模块,名为webDemo1 3、选择webDemo1右键,选择Add Framework Support 4、在WEB-INF下新建文件夹classes和lib 5、打开项目结构(Project Structure) 6、项目配置 7、模...
2023-03-09