多線程調用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的使用有了更深的了解。