有一天,
我正听着歌,开心的敲着代码,这时候小王同志急冲冲的跑过来拍了拍我的肩膀,
“付老板,我这遇到一个问题,连续两次请求同一个接口,参数传的不同,但是livedata的onChange回调只走了一次,UI界面上只有一个地方更新成功了,这是咋回事啊?”
一听有bug,这我可就来劲了,立马摘下耳机,
“来,上代码”,
“诶~ 你这是在UI层注册了一个监听,然后在请求接口的地方,利用livedata连续postValue两次。看情况你这应该是postValue搞的鬼,不急,我来模拟下场景,看看他的内部实现”。
新建一个ViewModel,创建一个String类型值的MutableLiveData,向外抛出一个更新String值的方法,并且使用liveData的postValue来通知UI更新数据。
class MyViewModel : ViewModel() {
val countLiveData = MutableLiveData<String>()
var title: String = ""
/**
* livedata postValue
*/
fun updateTitlePost(str:String) {
title = str
countLiveData.postValue(title)
}
}
activity上直接注册一个Observer,并且在button的click中连续两次调用viewModel中更新String值的方法。
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
viewModel.countLiveData.observe(this,object :Observer<String>{
override fun onChanged(t: String?) {
Log.d(TAG, "livedata onChanged: value = $t")
}
})
/**
* 模拟请求,连续调用两次,第一次更新值为‘Java’,
* 第二次更新值为‘Kotlin’
*/
findViewById<Button>(R.id.button).setOnClickListener {
viewModel.updateTitlePost("Java")
viewModel.updateTitlePost("Kotlin")
}
点击模拟接口请求,连续调用两次,第一次更新值为‘Java’,第二次更新值为‘Kotlin’。
运行看看,
可以看到控制台的log输出,不管点几次,只有Kotlin的值更新成功了,也就是符合小王同志的场景了,那第一次更新的Java值跑哪去了?
为什么连续postValue两次,第一次的值会丢失?
带着问题,我们来看看postValue内部的源码。
/**
* 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;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
诶~~ 值得注意的是,官方已经在postValue的注释上说明了,
If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
如果在主线程执行一个已发布的任务之前多次调用此方法,则只会分派最后一个值。
虽然官方直接解释了,我们还是要搞清楚原理不是。
接着看源码,postValue内部维护了一个boolean类型的postTask,利用synchronized对一个objec对象 mDataLock加了锁,并且内部有一个全局变量mPendingData,这是问题的关键。每次postValue会将新的值赋给mPendingData,然后会在一个Runnable中进行值的分发,且使用ArchTaskExecutor将该Runnable的任务发布到主线程中(题外话:这就是为什么postValue一直在主线程更新的原因)。
我通过断点的方式,跟踪了一下
viewModel.updateTitlePost("Java")
viewModel.updateTitlePost("Kotlin")
这两步的