持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
这篇文章是对LiveData更新数据方法 setValue()、postValue()的一个思考,不知道屏幕前的你有没有这样相同的疑惑?
postValue 与 setValue 有何区别,怎么用?
在LiveData的官方文档中有这么介绍:您必须调用 setValue(T) 方法以从主线程更新 LiveData 对象。如果在工作器线程中执行代码,您可以改用 postValue(T) 方法来更新 LiveData 对象。
也就是说这两个方法的主要区别就是,一个是工作在主线程的,而另一个是工作在子线程。
分别看看他们的源码:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
assertMainThread("setValue")方法就是用来判断当前线程是否处于主线程,如果不是,会抛出异常"Cannot invoke setValue on a background thread",也就是不能再子线程中调用setValue()方法。
如果处于主线程中,就将当前设置的最新数据分发给各个观察者对象。
接着再来看看postValue()方法的源码:
final Object mDataLock = new Object();
private volatile Object mData;
volatile Object mPendingData = NOT_SET;
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
只要涉及到子线程,就会存在线程安全问题,postValue()也不例外。所以这里出现了synchronized (mDataLock) ,锁住 mDataLock 对象,并且使用 volatile 关键字来修饰 mPendingData 待发送数据对象,保证线程间的及时可见性,从而来保证线程安全。
而且从源码中也可以看出,postValue()本质上也是通过setValue()来更新数据的,通过切换到主线程来执行setValue()。
一直用postValue()行不行?
既然postValue()本质上也是通过setValue()来更新数据的,而且当你不小心在子线程中使用setValue()来更新数据时,会抛出异常,那这样的话,我可不可以一直使用postValue()来更新数据呢?
来个简单例子跑一下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...省略代码...
dbinding.apply {
setValueBtn.setOnClickListener {
for (setValueNum in 0..100) {
Log.e("LiTest", "onCreate: setValueNum = $setValueNum")
GlobalState.testNumberLd.value = setValueNum
}
}
postValueBtn.setOnClickListener {
for (postValueNum in 0..100) {
Log.e("LiTest", "onCreate: postValueNum = $postValueNum")
GlobalState.testNumberLd.postValue(postValueNum)
}
}
GlobalState.testNumberLd.observe(this@TestHelperActivity, {
Log.e("LiTest", "********* onCreate: observe number = $it", )
})
}
}
我们先建一个Activity,放两个按钮,分别是setValueBtn按钮,用来调用setValue()方法,以及postValueBtn按钮,用来调用postValue()方法。点击按钮,分别进行100次数据更新。
思考一下,我们的观察者会分别观察到怎样的数据情况呢?
看看setValue()方法输出的日志情况:
E/LiTest: onCreate: setValueNum = 0
E/LiTest: ********* onCreate: observe number = 0
E/LiTest: onCreate: setValueNum = 1
E/LiTest: ********* onCreate: observe number = 1
E/LiTest: onCreate: setValueNum = 2
E/LiTest: ********* onCreate: observe number = 2
···
···
E/LiTest: onCreate: setValueNum = 99
E/LiTest: ********* onCreate: observe number = 99
E/LiTest: onCreate: setValueNum = 100
E/LiTest: ********* onCreate: observe number = 100
每次进行setValue()更新数据后,我们的观察者都能接收到最新的数据。
再来看看postValue()方法输出的日志情况:
E/LiTest: onCreate: postValueNum = 0
E/LiTest: onCreate: postValueNum = 1
E/LiTest: onCreate: postValueNum = 2
···
···
E/LiTest: onCreate: postValueNum = 99
E/LiTest: onCreate: postValueNum = 100
E/LiTest: ********* onCreate: observe number = 100
从日志可以看出,即使我们进行了100次postValue(),最终我们的观察者对象只观察到最后一次postValue()更新的数据。
这是当我们使用postValue()连续更新数据时,只有最后的数据会被更新,例如:
liveData.postValue("a");
liveData.postValue("b");
liveData.postValue("c");
最终,活跃的观察者对象们只会收到数据"c",而感知不到"a"与"b"。
总结
虽然我们可以一直使用postValue()方法来更新数据,它不像setValue()方法会抛出异常,所以就开发的难易程度来说,一直使用postValue()来更新数据会更加的简单。但是postValue()会丢失数据,而且它需要进行切换到主线程来调用setValue()方法来更新数据,所以就性能来说,肯定会造成更多的开销。所以我们不能一直使用postValue()方法来更新数据,而是需要合理的在主线程通过setValue()方法来更新数据,在子线程中通过postValue()方法来更新数据,这样也能让我们更好的理清我们的代码逻辑。
如果本文对你有任何帮助,请帮乐黎我点个赞呀,十分感谢。