多线程调用LiveData的postValue如何保证只展示最新值
LiveData简介
LiveData是一个数据持有类,它可以通过观察者模式被android的Activity和Fragment等监听,当LiveData的数据发生变化时,会通过onChanged方法通知页面更新数据。
LiveData的优势在于可以感知组件的生命周期,只有在组件是活跃状态时才会通知组件更新(observe方法),当组件被destroy后,就算LiveData发生了变化也不会回调通知,且destroy后自动取消了组件的监听,避免了内存泄露。
当发生Config Change时(比如屏幕旋转),不需要通过手动处理处理数据的保存( onSaveInstanceState ),LiveData会自动处理。
LiveData更新数据有两个方法,setValue和postValue
- setValue只能在主线程使用
- postValue可以在子线程和主线程使用,通过handler回到主线程设置
多线程使用postValue的问题
先贴上部分源代码
protected void postValue(T value) {
boolean postTask;
// 锁住
synchronized (mDataLock) {
// 当前没有人在处理 post 任务
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
// 处理完毕之后将 mPendingData 置为 NOT_SET
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};
注意一点:mPendingData的类型是private volatile Object的,在主线程的runnable中是用mPendingData的值来更新的。
关于postValue的使用官方文档有这么一句
If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
大概意思是在主线程执行更新从postValue传过来的值前(就是在runnable的锁代码块完成前),如果调用了多次postValue,那么只有最新一次的值会被更新。
postValue时如何保证这个的呢?
一开始我看这个很疑惑,因为现在假设有两个线程AB先后调用了同一个LiveData的postValue,先A后B,一开始A先拿到postValue中的锁,此时postTask为True,mPendingData变成了A设的值a,然后通过handler走到主线程去执行,但在A拿到runnable中的锁前,线程B提前拿到了postValue中的锁,此时postTask变为False,mPendingData变为了B设的值b,此时B释放锁,然后因为postTask为false直接return,最后A再在主线程更新,这时不就是用值a来更新了吗?这怎么能做到只有最新一次的值被更新?
LiveData通过volatile保证多次postValue只会更新一次最新值
是的,因为mPendingData是volatile的,所以上述情况下,B更新mPendingData后,主线程中的mPendingData也会马上更新从a为b(实际情况其实是读的时候从内存读时才更新),这样的话值a就被刷新成更新的b了,保证了多线程多次postValue只有最新的值被更新
结论
LiveData的postValue通过volatile保证了同一个LiveData变量多次调用下也只会更新最新的值,通过阅读liveData的源代码对多线程锁和volatile的使用有了更深的理解。