前言
LiveData是Android常用的組件,它代表具有生命周期的數(shù)據(jù),是MVVM框架組成不可或缺的一部分

問題場景
在我們的代碼案例中,我們點擊A頁面的List中的一個Item,然后將數(shù)據(jù)傳遞并跳轉(zhuǎn)到B Fragment,然后在B Fragment中使用LiveData對數(shù)據(jù)進(jìn)行業(yè)務(wù)處理從而展示,但是由于編碼問題導(dǎo)致當(dāng)點擊某一條Item出錯時,點擊后續(xù)的Item都無法正常顯示B Fragment的UI造成白屏的現(xiàn)象,由于這個問題我對LIveData的數(shù)據(jù)通知方式的源碼做了分析
源碼分析
當(dāng)我們對LivaData進(jìn)行賦值并且它處于活動狀態(tài)時會調(diào)用
@MainThread
protected void setValue(T value) {
//斷言此函數(shù)是在主線程執(zhí)行,否則會拋出異常
assertMainThread("setValue");
//將此LiveData的數(shù)據(jù)版本加一
mVersion++;
//LiveData中真實存儲內(nèi)容的字段,getValue返回的就是此字段的值
mData = value;
//進(jìn)行數(shù)據(jù)通知處理
dispatchingValue(null);
}
dispatchingValue函數(shù)中有兩個重要的變量,mDispatchingValue和mDispatchInvalidated,并且這兩個字段只有在這個函數(shù)中有使用,之所以不做成局部變量是因為這兩個字段控制的是所有此LiveData數(shù)據(jù)分發(fā)的行為,而不僅是本次通知的行為
mDispatchingValue表示數(shù)據(jù)是否正在調(diào)度中,如果上一次設(shè)置的數(shù)據(jù)正在分發(fā)中則會return掉本次數(shù)據(jù)通知操作
mDispatchInvalidated表示數(shù)據(jù)調(diào)度是否失效,如果在本次設(shè)置數(shù)據(jù)調(diào)度的過程中又收到新的調(diào)度,則本次調(diào)度就會失效
void dispatchingValue(@Nullable ObserverWrapper initiator) {
//是否正在進(jìn)行數(shù)據(jù)調(diào)度
if (mDispatchingValue) {
//如果正在進(jìn)行數(shù)據(jù)調(diào)度則上次正在執(zhí)行的數(shù)據(jù)調(diào)度標(biāo)記已失效
mDispatchInvalidated = true;
//如果正在進(jìn)行數(shù)據(jù)調(diào)度則本次數(shù)據(jù)調(diào)度直接return取消
return;
}
//將是否正在進(jìn)行數(shù)據(jù)調(diào)度的值設(shè)置為true,表示數(shù)據(jù)正在調(diào)度中
mDispatchingValue = true;
//下面這個循環(huán)就是數(shù)據(jù)調(diào)度的過程,它會遍歷所有注冊的觀察者,對他們通知新的數(shù)據(jù)
do {
//在數(shù)據(jù)調(diào)度開始時將是否失效字段置為false
mDispatchInvalidated = false;
//當(dāng)調(diào)用setValue時initiator為null
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
//內(nèi)層循環(huán)遍歷所有的觀察者考慮通知新的數(shù)據(jù)
considerNotify(iterator.next().getValue());
//一旦發(fā)現(xiàn)數(shù)據(jù)失效,則停止遍歷觀察者停止數(shù)據(jù)通知
if (mDispatchInvalidated) {
break;
}
}
}
//外層循環(huán)的執(zhí)行條件是數(shù)據(jù)失效,也就是說如果在通知數(shù)據(jù)的過程中如果數(shù)據(jù)失效則會重新執(zhí)行一次do代碼塊,從而通知最新的數(shù)據(jù)
} while (mDispatchInvalidated);
//表示數(shù)據(jù)調(diào)度結(jié)束,開關(guān)打開,可以接受下一次的數(shù)據(jù)設(shè)置
mDispatchingValue = false;
}
從dispatchingValue函數(shù)值你給我們可以看到
1.mDispatchingValue 字段用于表示數(shù)據(jù)通知是否正在執(zhí)行中
2.如果在執(zhí)行中又來了一條數(shù)據(jù)雖然由于mDispatchingValue 的緣故retun了本次執(zhí)行,但是由于失效字段mDispatchInvalidated的控制do代碼塊會多次執(zhí)行,從而只會通知最新的數(shù)據(jù),這也就是為什么我們連續(xù)設(shè)置Livedata的值只有最后一次會生效的原因
considerNotify函數(shù)表示目標(biāo)觀察者考慮是否要通知數(shù)據(jù)更新
private void considerNotify(ObserverWrapper observer) {
//如果觀察者的生命周期不可用則放棄此次通知,生命周期依賴于Activity或者Fragment的生命周期
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
//再次檢查是否為非活動狀態(tài),并且額外的通知一次狀態(tài)變更
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//如果觀察者的數(shù)據(jù)版本已經(jīng)高于此次數(shù)據(jù)版本,則取消
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
//調(diào)用onChanged函數(shù),通知此次數(shù)據(jù)變更
observer.mObserver.onChanged((T) mData);
}
3.在函數(shù)dispatchingValue中如果數(shù)據(jù)過期,會在調(diào)用considerNotify之后才調(diào)用了break,這并不會造成通知舊數(shù)據(jù)的漏洞,因為在considerNotify函數(shù)中直接通知的最新的mVersion以及mData
問題解決
我們可以看到considerNotify函數(shù)的最終目的是要去調(diào)用onChanged函數(shù),也就是我們監(jiān)聽LiveData的代碼塊,我們會發(fā)現(xiàn)如果onChanged代碼塊中有異常拋出則函數(shù)dispatchingValue會直接從considerNotify(iterator.next().getValue())處直接中斷,造成mDispatchingValue的值無法變回false狀態(tài),以致于此LiveData再也無法正常的進(jìn)行數(shù)據(jù)通知的操作一直停留在了工作中的狀態(tài),造成了一種類似死鎖的現(xiàn)象
4.為了防止dispatchingValue造成的死鎖,請關(guān)注你的onChanged代碼塊防止有異常出現(xiàn)破壞數(shù)據(jù)通知的邏輯
這也就印證了為什么在我們的業(yè)務(wù)中當(dāng)一條Item出錯時,再點擊其他的Item無法正常收到LiveData更新的現(xiàn)象,所以解決的方案就是在onChanged函數(shù),也就是我們監(jiān)聽LiveData的代碼塊不要有異常拋出進(jìn)行異常修復(fù)或者特殊場景進(jìn)行try catch,要么就使用不會拋出異常的簡單業(yè)務(wù)
總結(jié)
1.mDispatchingValue 字段用于表示數(shù)據(jù)通知是否正在執(zhí)行中,防止數(shù)據(jù)通知操作重復(fù)
2.如果在執(zhí)行中又來了一條數(shù)據(jù)雖然由于mDispatchingValue 的緣故retun了本次執(zhí)行,但是由于失效字段mDispatchInvalidated的控制do代碼塊會多次執(zhí)行,從而只會通知最新的數(shù)據(jù),這也就是為什么我們連續(xù)設(shè)置Livedata的值只有最后一次會生效的原因
3.在函數(shù)dispatchingValue中如果數(shù)據(jù)過期,會在調(diào)用considerNotify之后才調(diào)用了break,這并不會造成通知舊數(shù)據(jù)的漏洞,因為在considerNotify函數(shù)中直接通知的最新的mVersion以及mData
4.為了防止dispatchingValue造成的死鎖,請關(guān)注你的onChanged代碼塊防止有異常出現(xiàn)破壞數(shù)據(jù)通知的邏輯
三個函數(shù)不是很復(fù)雜的系統(tǒng)函數(shù)實現(xiàn)了數(shù)據(jù)的防重復(fù)通知,連續(xù)多次通知只取最后一次的性能優(yōu)化,在數(shù)據(jù)非活動狀態(tài)時不更新三個強(qiáng)大的LiveData的基礎(chǔ)功能,這是我們在開發(fā)中設(shè)計代碼結(jié)構(gòu)的一個很好的學(xué)習(xí)案例,比如可以應(yīng)用到用戶連續(xù)需點擊多次提交只取最后一次的業(yè)務(wù)需求中
歡迎關(guān)注Mike的簡書
Android知識整理