本文作者:我的Android 翻山之路,原文发布于:Android 翻山之路。
做个实验
class MainActivity : AppCompatActivity() {
private val liveData = MutableLiveData<Int>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 订阅观察者
liveData.observe(this) {
// 控制台打印 观察者收到的值
Log.d("MainActivity", "observer: $it")
}
// for 循环调用 postValue
for (i in 0..2) {
Log.d("MainActivity", "postValue: $i")
liveData.postValue(i)
}
}
}
来看看控制台:
只有 for 循环最后一次 postValue 的值 2 打印了出来。为什么不是 0、1、2 都打印出来呢?
LiveData 有 setValue 和 postValue 两种通知观察者的方法,postValue 从上看有点问题?那我们来试试 setValue。
将上面的实验代码 postValue 改为 setValue:
class MainActivity : AppCompatActivity() {
private val liveData = MutableLiveData<Int>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 订阅观察者
liveData.observe(this) {
// 控制台打印 观察者收到的值
Log.d("MainActivity", "observer: $it")
}
// for 循环调用 setValue
for (i in 0..2) {
liveData.setValue(i)
}
}
}
再看看控制台:
怎么也是这样呢。严重怀疑 setValue 和 postValue 有毛病,得治。
这两次实验都是在 onCreate 里完成的,要不要尝试把调用 postValue 和 setValue 的位置换一换呢,算了,说干就干:加一个按钮,点击按钮时再循环 postValue:
class MainActivity : AppCompatActivity() {
private val liveData = MutableLiveData<Int>()
private val btn_set_value: Button by lazy { findViewById(R.id.btn_set_value) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
liveData.observe(this) {
Log.d("MainActivity", "observer: $it")
}
btn_set_value.setOnClickListener {
for (i in 0..2) {
Log.d("MainActivity", "postValue: $i")
liveData.postValue(i)
}
}
}
}
点击按钮之后的控制台:
还有什么值得留恋。。。
洗把脸,清醒清醒postValue 不行,再试试 setValue。
class MainActivity : AppCompatActivity() {
private val liveData = MutableLiveData<Int>()
private val btn_set_value: Button by lazy { findViewById(R.id.btn_set_value) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
liveData.observe(this) {
Log.d("MainActivity", "liveData: $it")
}
btn_set_value.setOnClickListener {
for (i in 0..10) {
Log.d("MainActivity", "setValue: $i")
liveData.setValue(i)
}
}
}
}
小心翼翼的点击 button,如看考试成绩般一点点的拉开控制台:
可害的好苦啊,终于他变了,而我也变的情绪失控。
为什么!!!
小小总结一下
总结上述 4 种方式的实验结果如下:
1. onCreate 方法中调用 postValue 和 setValue 结果一样,observer 都是只收到了 for 循环最后一次的值。
2. 点击按钮时循环调用 postValue 和 setValue 时结果不一样,postValue 只收到了最后一次的值,而 setValue 则收到了所有 for 循环的值。
为什么会发生这样令人困惑的结果的呢?
一点点来看
先看下 LiveData.setValue 调用后,怎么到的 observe 的 observer.onChanged 方法
private int mVersion;
private volatile Object mData;
/**
* Sets the value. If there are active observers, the value will be dispatched to them.
* <p>
* This method must be called from the main thread. If you need set a value from a background
* thread, you can use {@link #postValue(Object)}
*
* @param value The new value
*/
@MainThread
protected void setValue(T value) {
// 必须主线程调用
assertMainThread("setValue");
// LiveData 的成员属性 mVersion 数据版本号 +1
mVersion++;
// 将 value 赋值给 LiveData 的成员属性 mData;
mData = value;
// 分发 value
dispatchingValue(null);
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
// initiator 为 null 所以会走到这里
// 遍历观察者
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// 通知
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
// ObserverWrapper 是 Observer 的包装类,这里不再延伸
private void considerNotify(ObserverWrapper observer) {
// 判断 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;
}
// 校验 Observer 的版本号是否小于 LiveData 的版本号,版本号是 LiveData 粘性的关键
if (observer.mLastVersion >= mVersion) {
return;
}
// LiveData 版本号赋值给 Observer 的版本号
observer.mLastVersion = mVersion;
// 这里这里 终于是看到了 将缓存的值 mData 传递给了 Observer 的 onChanged 方法。也就是 LiveData.observe 传入对象 Observer 的 onChanged 方法。
observer.mObserver.onChanged((T) mData);
}
看完上面代码的注释我们知道了调用 setValue 后,到 Observer 的 onChanged 方法被调用的过程。
中间有个关键点,最后 considerNotify 方法里会判断 !observer.mActive 。判断 observer 是否在活跃状态,不活跃则不往下走。
来看看什么是活跃,什么是不活跃。
// ObserverWrapper 是 Observer 的包装类
private abstract class ObserverWrapper {
// LiveData.observe 方法传入的 Observer 对象
final Observer<? super T> mObserver;
// 这是我们要分析的 是否活跃的成员属性
boolean mActive;
int mLastVersion = START_VERSION;
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;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
// 这里改变的 mActive 值
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}
}
activeStateChanged 方法又是在哪儿调用的呢?
需要先简单看下 LiveData 的 observe 方法:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 创建了 LifecycleBoundObserver 对象,传入我们的 Observer 对象到构造函数
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 将 LifecycleBoundObserver 对象添加观察 Lifecycle 生命周期
owner.getLifecycle().addObserver(wrapper);
}
// LifecycleBoundObserver 继承了 ObserverWrapper,并实现了 LifecycleEventObserver 接口用于观察生命周期事件。
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() {
// 返回当前生命周期至少是 STARTED 状态才为 true,否则 false
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
// 这里看到调用了父类 ObserverWrapper 的 activeStateChanged 对象,但是传入的值是 shouldBeActive 方法的返回值,shouldBeActive 方法也是父类 ObserverWrapper 的方法,但是是抽象方法,shouldBeActive 在 LifecycleBoundObserver 中实现在上面
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
需要看 mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED) 方法怎么判断的。
/**
* Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached in two cases:
* <ul>
* <li>after {@link android.app.Activity#onStart() onStart} call;
* <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
* </ul>
*/
STARTED,
看看注释是怎么解释的:
在两种情况下达到此状态:调用 onStart 后;就在 onPause 调用之前。众所周知安卓 Activity 生命周期如图:
现在解决一个困惑:为什么点击按钮时循环调用 setValue 时,收到了所有 for 循环的值。
这时候就可以解答一下了:
因为 LiveData 的观察生命周期的特性,只有在页面活跃状态下才可以分发值,当可以点击按钮时候,当前Activity 的生命周期已经走过了 OnStart、onResume,满足当前生命周期至少是 STARTED 的状态,setValue 到最终分发值一路畅通无阻,所以 for 循环中,每 set Value 一次,最终都会调用 Observer 的 onChanged 方法。
为什么在 onCreate 方法中循环调用 setValue 最终只通知了最后一个值呢?再回顾下 setValue 的流程:
private int mVersion;
private volatile Object mData;
/**
* Sets the value. If there are active observers, the value will be dispatched to them.
* <p>
* This method must be called from the main thread. If you need set a value from a background
* thread, you can use {@link #postValue(Object)}
*
* @param value The new value
*/
@MainThread
protected void setValue(T value) {
// 必须主线程调用
assertMainThread("setValue");
// LiveData 的成员属性 mVersion 数据版本号 +1
mVersion++;
// 每次调用 setValue 都会将 value 赋值给 LiveData 的成员属性 mData;这里不会关心value 是否已经分发
mData = value;
// 分发 value
dispatchingValue(null);
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
...
// 通知
considerNotify(iterator.next().getValue());
...
}
// ObserverWrapper 是 Observer 的包装类,这里不再延伸
private void considerNotify(ObserverWrapper observer) {
// 判断 observer 是否在活跃状态,不活跃则不往下走,
if (!observer.mActive) {
return;
}
...
// 校验 Observer 的版本号是否小于 LiveData 的版本号,版本号是 LiveData 粘性的关键
if (observer.mLastVersion >= mVersion) {
return;
}
// LiveData 版本号赋值给 Observer 的版本号
observer.mLastVersion = mVersion;
// 这里这里 终于是看到了 将缓存的值 mData 传递给了 Observer 的 onChanged 方法。也就是 LiveData.observe 传入对象 Observer 的 onChanged 方法。
observer.mObserver.onChanged((T) mData);
}
最终 considerNotify 方法中判断 !observer.mActive 条件满足,直接 return了,Observer 对象的 mLastVersion 也不会被赋值,始终为 -1。
因为在 onCreate 方法中调用的 setValue,observer.mActive 一定为 false, 而且最终 for 循环走完,LiveData 的 mVersion 为 2,mData 为 2,这解释了最终 LiveData 的 mData 值为 2,但是还不知道什么时候 Observer 的 onChanged 方法被调用。
上面分析我们知道,LiveData 调用 observe 时,observe 方法内,会将 Observer 对象包装为可感知生命周期的 LifecycleBoundObserver 对象,在 onStateChanged 中接受生命周期事件的改变,方法内调用 LifecycleBoundObserver 的父类的 activeStateChanged 方法,传入的是 shouldBeActive() 方法的返回值,而 shouldBeActive() 方法判断 isAtLeast(STARTED),也就是 调用 onStart 后;就在 onPause 调用之前 是返回 true。
void activeStateChanged(boolean newActive) {
// 值改变时才会走
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
// 如果从不活跃变为活跃状态 就会调用 dispatchingValue
if (mActive) {
dispatchingValue(this);
}
}
这不就破案了吗
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// 这里 initiator 不为null
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;
}
@SuppressWarnings("unchecked")
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;
}
// 现在 observer 的 mLastVersion 值为 -1, 而 mVersion 值为 2,条件不通过
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 调用了 onChanged 传入了最新的值 mData 2
observer.mObserver.onChanged((T) mData);
}
这下又解决了个疑问🤔️:onCreate 方法中循环调用 setValue,Observer 方法只收到了最后一次的值。
现在 setValue 的两次实验都能解释的清楚,剩下 postValue 的两种情况怎么解释呢?
再看下 LiveData.postValue 调用后,怎么到的 observe 的 observer.onChanged 方法
final Object mDataLock = new Object();
static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET;
/**
* Posts a task to a main thread to set the given value. So if you have a following code
* executed in the main thread:
* <pre class="prettyprint">
* liveData.postValue("a");
* liveData.setValue("b");
* </pre>
* The value "b" would be set at first and later the main thread would override it with
* the value "a".
* <p>
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*
* @param value The new value
*/
protected void postValue(T value) {
boolean postTask;
// 同步代码块 使用 mDataLock 对象当锁
synchronized (mDataLock) {
// 如果 mPendingData 等于 NOT_SET 值 postTask 才为 true
postTask = mPendingData == NOT_SET;
// postValue 传的值赋值给成员属性 mPendingData
mPendingData = value;
}
// 若 postTask 为 false 则 return
if (!postTask) {
return;
}
// 发送一个消息到主线程 handler,
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
// 主线程消息队列执行到 Message 时调用的 runnable
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
// 同步锁
synchronized (mDataLock) {
// 将 mPendingData 赋值给 newValue 对象
newValue = mPendingData;
// 将 mPendingData 对象值重置为 NOT_SET
mPendingData = NOT_SET;
}
// 这里又调用了 setValue,之后就跟 LiveData.setValue 流程一样了。
setValue((T) newValue);
}
};
postValue 最终调用了 setValue,但是比 setValue 多了一步,就是往主线程消息队列插入了一条消息,等消息队列执行到这条消息时,才会调用 setValue。
onCreate 中 for 循环调用 postValue 为什么只有最后一个值输出呢?
分析一下下:我们知道 onCreate 方法是在主线程中被调用的(至于为啥可查询 Activity 启动流程,这里不分析),也是主线程消息队列的一条消息,消息队列会在上一条消息执行结束后才执行下一条消息。那么在 onCreate 中执行的 for 循环,是不是得等循环完毕才会往下走呢,答案当然是啦。再看这段代码:
final Object mDataLock = new Object();
static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET;
/**
* Posts a task to a main thread to set the given value. So if you have a following code
* executed in the main thread:
* <pre class="prettyprint">
* liveData.postValue("a");
* liveData.setValue("b");
* </pre>
* The value "b" would be set at first and later the main thread would override it with
* the value "a".
* <p>
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*
* @param value The new value
*/
protected void postValue(T value) {
boolean postTask;
// 同步代码块 使用 mDataLock 对象当锁
synchronized (mDataLock) {
// 如果 mPendingData 等于 NOT_SET 值 postTask 才为 true
postTask = mPendingData == NOT_SET;
// postValue 传的值赋值给成员属性 mPendingData
mPendingData = value;
}
// 若 postTask 为 false 则 return
if (!postTask) {
return;
}
// 发送一个消息到主线程 handler,
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
• for 循环第一次调用 postValue 值 0 时,LiveData 的 mPendingData = NOT_SET, postTask = mPendingData == NOT_SET 为 true,mPendingData 赋值为 value 0,下面判断条件 !postTask 为 false,往消息队列插入一条消息 Runable 对象为 mPostValueRunnable。
• for 循环第二次调用 postValue 值 1 时,LiveData 的 mPendingData = 0,postTask = mPendingData == NOT_SET 为 false,mPendingData 赋值为 value 1,下面判断条件 !postTask 为 true,直接 return 不会再向主线程发消息咯。
• for 循环第三次调用 postValue 值 2 时,LiveData 的 mPendingData = 1,postTask = mPendingData == NOT_SET 为 false,mPendingData 赋值为 value 2,下面判断条件 !postTask 为 true,直接 return 也不会再向主线程发消息。
到此 for 循环执行完毕,静等主线程执行完这条消息,等到执行 mPostValueRunnable 时:
// 主线程消息队列执行到 Message 时调用的 runnable
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
// 同步锁
synchronized (mDataLock) {
// 将 mPendingData 赋值给 newValue 对象
newValue = mPendingData;
// 将 mPendingData 对象值重置为 NOT_SET
mPendingData = NOT_SET;
}
// 这里又调用了 setValue,之后就跟 LiveData.setValue 流程一样了。
setValue((T) newValue);
}
};
这时 mPendingData 的值为 2 !!!,又将 mPendingData 对象值重置为 NOT_SET,最后调用 setValue 方法将 2 传了进去。再之后就跟 setValue 逻辑一样了。
到这儿是不是解决了 postValue分别在 onCreate 和 点击事件 时循环调用只收到最后一个值的疑问🤔️呢?