Android Jetpack 组件LiveData源码解析
2023-03-10 17:35:35 来源:易采站长站 作者:
目录前言基本使用疑问源码分析ObserverObserverWrapperLifecycleBoundObserverMutableLiveDatapostValuesetValue问题答疑LiveD...
目录
前言基本使用
疑问
源码分析
Observer
ObserverWrapper
LifecycleBoundObserver
MutableLiveData
postValue
setValue
问题答疑
LiveData 特性引出的问题
问题解决
最后
前言
本文来分析下 LiveData 的源码,以及其在实际开发中的一些问题。
基本使用
一般来说 LiveData 都会配合 ViewModel 使用,篇幅原因关于 ViewModel 的内容将在后续博客中分析,目前可以将 ViewModel 理解为一个生命周期比 Activity 更长的对象,且不会造成内存泄漏。
示例代码:
MainViewModel.kt
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
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 非常简单就不贴了,看下效果图:

疑问
很简单的功能,但是有两个问题需要注意:
在 XML 中并没有给中间的 TextView 设置 text 属性,仅仅给 LiveData 赋值了初始值 0,就可以直接显示到 TextView 上;数值发生变化后,进行横竖屏切换后 TextView 依然保持着最新值(如果 number 作为普通 Int 放在 Activity 中,当 Activity 由于横竖屏切换导致重建会重新变为 0);
本文将以这两个问题作为切入点,对 LiveData 源码进行分析。
源码分析
Observer
从实例代码中很容易看出这是典型的观察者模式,当 LiveData 发生变化时会对其订阅者发送通知,将最新值传递过去,Observer 就相当于其观察者,先来看一下 Observer 接口:
public interface Observer<T> {
void onChanged(T t);
}
当 LiveData 发生变化时,就会触发其观察者的 onChanged 方法,并传递最新值;
再看一下其添加订阅时的源码:
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 容器中存储;
ObserverWrapper
接着来看一下 LiveData 是如何对 Observer 进行包装的,LifecycleBoundObserver 实现了 ObserverWrapper,那么就先来看看 ObserverWrapper 的源码:
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
接着看一下 LifecycleBoundObserver 的源码:
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
上述的观察者相关的重要源码已经分析完,接着来看一下示例代码中定义的 MutableLiveData 源码:
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
先来看看 postValue:
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 具体执行了什么:
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 中获取的也是最新值。
setValue
postValue 内部最终调用了 setValue,那么就来看看 setValue 的源码:
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:
// 用于保存其观察者 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 { /www.cppcns.com/ 遍历自身的观察者
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 的源码并不复杂,总结一下:
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 包装一层,在分发时判断并修改 AtomicBooleajsn 为 false。
我觉得这也是比较好的规避问题的方法,这里就随便贴一个了:
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 是最佳的选择;
以上就是Android Jetpack 组件LiveData源码解析的详细内容,更多关于Android Jetpack LiveData的资料请关注我们其它相关文章!













闽公网安备 35020302000061号