持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
前言
注意:您必须调用
setValue(T)
方法以从主线程更新LiveData
对象。如果在工作器线程中执行代码,您可以改用postValue(T)
方法来更新LiveData
对象。
这段描述来自Android官方文档的 LiveData 概览,如果你熟悉这二者方法之间的区别,可以先查看使用setValue会报错,那就一直使用postValue行不行?这篇文章了解一下先。
因为今天又有一个新的思考点,那就是:为什么Google不自己进行判断线程,然后合理使用setValue()
与postValue()
呢?。
实践
在开始实践前,我们先想一下,判断当前线程是否处于主线程中的代码应该怎么写呢?
答案在LiveData
的源码中。
参考方法:
static void assertMainThread(String methodName) {
if (!ArchTaskExecutor.getInstance().isMainThread()) {
throw new IllegalStateException("Cannot invoke " + methodName + " on a background thread");
}
}
public boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
根据当前线程的Looper
对象与MainLooper
进行对比,从而判断当前线程是否是主线程。
OK,接下来我们自己继承LiveData
,实现一个可以自动识别当前线程,当方法处于子线程中就使用postValue
,方法处于主线程中就使用setValue
。
public class LiMutableLiveData <T> extends LiveData<T> {
private static final String TAG = "LiMutableLiveData";
/**
* Creates a MutableLiveData initialized with the given {@code value}.
*
* @param value initial value
*/
public LiMutableLiveData(T value) {
super(value);
}
/**
* Creates a MutableLiveData with no value assigned to it.
*/
public LiMutableLiveData() {
super();
}
@Override
public void postValue(T value) {
Log.e(TAG, "postValue: ");
super.postValue(value);
}
@Override
public void setValue(T value) {
Log.e(TAG, "setValue: ");
super.setValue(value);
}
@SuppressLint("RestrictedApi")
public void updateValue(T value) {
Log.e(TAG, "addValue: value = " + value);
if (!ArchTaskExecutor.getInstance().isMainThread()) {
postValue(value);
} else {
setValue(value);
}
}
}
在updateValue(T)
方法中,我们先进行判断当前方法处于的线程,如果是主线程就调用setVaue()
方法,反之处于子线程就调用postValue()
方法。
然后我们测试一下:当我们点击开始按钮时,先在主线程中调用两次setValue()
方法,然后再开启一个子线程来调用两次postValue()
方法来更新数据。
class LiTestActivity : AppCompatActivity() {
private val TAG = "LiTestActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_li_test)
liTestNumberLd.observe(this, {
Log.e(TAG, "initObserve: liTestNumber = $it")
})
findViewById<Button>(R.id.startBtn).setOnClickListener {
liTestNumberLd.updateValue(6)
liTestNumberLd.updateValue(8)
Thread {
liTestNumberLd.updateValue(100)
liTestNumberLd.updateValue(200)
}.start()
}
}
}
接着看看输出日志是否如我们预期所想,输出日志如下:
E/LiMutableLiveData: updateValue: value = 6
E/LiMutableLiveData: setValue:
E/LiTestActivity: initObserve: liTestNumber = 6
E/LiMutableLiveData: updateValue: value = 8
E/LiMutableLiveData: setValue:
E/LiTestActivity: initObserve: liTestNumber = 8
E/LiMutableLiveData: updateValue: value = 100
E/LiMutableLiveData: postValue:
E/LiMutableLiveData: updateValue: value = 200
E/LiMutableLiveData: postValue:
E/LiMutableLiveData: setValue:
E/LiTestActivity: initObserve: liTestNumber = 200
从日志中可以看出,updateValue()
方法工作达到预期效果,当方法处于主线程中时就调用setValue()
方法进行数据更新。反之,当方法处于子线程中时就调用postValue()
方法进行数据更新,不过postValue()
最终还是走setValue()
。
那为什么Google不这么设计呢?
很多时候也不是库做的事情越多越好吧,虽然LiveData
可以做到自己判断一下当前方法所处的线程,然后合理调用setValue()
方法与postValue()
方法。但如果从性能的角度进行考虑,用户可以提前知道当前方法所处的线程,完全没必要做一次线程判断,所以Google这么做是从性能角度考虑?
其实我也不知道为啥Google要这么设计,屏幕前的你有任何想法吗?欢迎探讨。