前言
LiveData
是Android
常用的組件芬为,它代表具有生命周期的數(shù)據(jù)欢搜,是MVVM
框架組成不可或缺的一部分
問(wèn)題場(chǎng)景
在我們的代碼案例中,我們點(diǎn)擊A頁(yè)面
的List
中的一個(gè)Item
,然后將數(shù)據(jù)傳遞并跳轉(zhuǎn)到B Fragment
恃疯,然后在B Fragment
中使用LiveData
對(duì)數(shù)據(jù)進(jìn)行業(yè)務(wù)處理從而展示儿捧,但是由于編碼問(wèn)題導(dǎo)致當(dāng)點(diǎn)擊某一條Item
出錯(cuò)時(shí)荚坞,點(diǎn)擊后續(xù)的Item
都無(wú)法正常顯示B Fragment
的UI
造成白屏的現(xiàn)象,由于這個(gè)問(wèn)題我對(duì)LIveData
的數(shù)據(jù)通知方式的源碼做了分析
源碼分析
當(dāng)我們對(duì)LivaData
進(jìn)行賦值并且它處于活動(dòng)狀態(tài)時(shí)會(huì)調(diào)用
@MainThread
protected void setValue(T value) {
//斷言此函數(shù)是在主線程執(zhí)行,否則會(huì)拋出異常
assertMainThread("setValue");
//將此LiveData的數(shù)據(jù)版本加一
mVersion++;
//LiveData中真實(shí)存儲(chǔ)內(nèi)容的字段菲盾,getValue返回的就是此字段的值
mData = value;
//進(jìn)行數(shù)據(jù)通知處理
dispatchingValue(null);
}
dispatchingValue
函數(shù)中有兩個(gè)重要的變量颓影,mDispatchingValue
和mDispatchInvalidated
,并且這兩個(gè)字段只有在這個(gè)函數(shù)中有使用懒鉴,之所以不做成局部變量是因?yàn)檫@兩個(gè)字段控制的是所有此LiveData
數(shù)據(jù)分發(fā)的行為诡挂,而不僅是本次通知的行為
mDispatchingValue
表示數(shù)據(jù)是否正在調(diào)度中碎浇,如果上一次設(shè)置的數(shù)據(jù)正在分發(fā)中則會(huì)return
掉本次數(shù)據(jù)通知操作
mDispatchInvalidated
表示數(shù)據(jù)調(diào)度是否失效,如果在本次設(shè)置數(shù)據(jù)調(diào)度的過(guò)程中又收到新的調(diào)度璃俗,則本次調(diào)度就會(huì)失效
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;
//下面這個(gè)循環(huán)就是數(shù)據(jù)調(diào)度的過(guò)程,它會(huì)遍歷所有注冊(cè)的觀察者城豁,對(duì)他們通知新的數(shù)據(jù)
do {
//在數(shù)據(jù)調(diào)度開(kāi)始時(shí)將是否失效字段置為false
mDispatchInvalidated = false;
//當(dāng)調(diào)用setValue時(shí)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ù)失效,也就是說(shuō)如果在通知數(shù)據(jù)的過(guò)程中如果數(shù)據(jù)失效則會(huì)重新執(zhí)行一次do代碼塊唱星,從而通知最新的數(shù)據(jù)
} while (mDispatchInvalidated);
//表示數(shù)據(jù)調(diào)度結(jié)束雳旅,開(kāi)關(guān)打開(kāi),可以接受下一次的數(shù)據(jù)設(shè)置
mDispatchingValue = false;
}
從dispatchingValue
函數(shù)值你給我們可以看到
1.mDispatchingValue
字段用于表示數(shù)據(jù)通知是否正在執(zhí)行中
2.如果在執(zhí)行中又來(lái)了一條數(shù)據(jù)雖然由于mDispatchingValue 的緣故retun了本次執(zhí)行间聊,但是由于失效字段mDispatchInvalidated
的控制do
代碼塊會(huì)多次執(zhí)行攒盈,從而只會(huì)通知最新的數(shù)據(jù),這也就是為什么我們連續(xù)設(shè)置Livedata
的值只有最后一次會(huì)生效的原因
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.
//再次檢查是否為非活動(dòng)狀態(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ù)過(guò)期偷遗,會(huì)在調(diào)用considerNotify
之后才調(diào)用了break
,這并不會(huì)造成通知舊數(shù)據(jù)的漏洞,因?yàn)樵?code>considerNotify函數(shù)中直接通知的最新的mVersion
以及mData
問(wèn)題解決
我們可以看到considerNotify
函數(shù)的最終目的是要去調(diào)用onChanged
函數(shù),也就是我們監(jiān)聽(tīng)LiveData
的代碼塊,我們會(huì)發(fā)現(xiàn)如果onChanged
代碼塊中有異常拋出則函數(shù)dispatchingValue
會(huì)直接從considerNotify(iterator.next().getValue())
處直接中斷驼壶,造成mDispatchingValue
的值無(wú)法變回false
狀態(tài),以致于此LiveData
再也無(wú)法正常的進(jìn)行數(shù)據(jù)通知的操作一直停留在了工作中的狀態(tài)氏豌,造成了一種類似死鎖的現(xiàn)象
4.為了防止dispatchingValue
造成的死鎖,請(qǐng)關(guān)注你的onChanged
代碼塊防止有異常出現(xiàn)破壞數(shù)據(jù)通知的邏輯
這也就印證了為什么在我們的業(yè)務(wù)中當(dāng)一條Item
出錯(cuò)時(shí)热凹,再點(diǎn)擊其他的Item
無(wú)法正常收到LiveData
更新的現(xiàn)象泵喘,所以解決的方案就是在onChanged
函數(shù),也就是我們監(jiān)聽(tīng)LiveData
的代碼塊不要有異常拋出進(jìn)行異常修復(fù)或者特殊場(chǎng)景進(jìn)行try catch
,要么就使用不會(huì)拋出異常的簡(jiǎn)單業(yè)務(wù)
總結(jié)
1.mDispatchingValue
字段用于表示數(shù)據(jù)通知是否正在執(zhí)行中般妙,防止數(shù)據(jù)通知操作重復(fù)
2.如果在執(zhí)行中又來(lái)了一條數(shù)據(jù)雖然由于mDispatchingValue 的緣故retun了本次執(zhí)行纪铺,但是由于失效字段mDispatchInvalidated
的控制do
代碼塊會(huì)多次執(zhí)行,從而只會(huì)通知最新的數(shù)據(jù)碟渺,這也就是為什么我們連續(xù)設(shè)置Livedata
的值只有最后一次會(huì)生效的原因
3.在函數(shù)dispatchingValue
中如果數(shù)據(jù)過(guò)期鲜锚,會(huì)在調(diào)用considerNotify
之后才調(diào)用了break
,這并不會(huì)造成通知舊數(shù)據(jù)的漏洞,因?yàn)樵?code>considerNotify函數(shù)中直接通知的最新的mVersion
以及mData
4.為了防止dispatchingValue
造成的死鎖苫拍,請(qǐng)關(guān)注你的onChanged
代碼塊防止有異常出現(xiàn)破壞數(shù)據(jù)通知的邏輯
三個(gè)函數(shù)不是很復(fù)雜的系統(tǒng)函數(shù)實(shí)現(xiàn)了數(shù)據(jù)的防重復(fù)通知芜繁,連續(xù)多次通知只取最后一次的性能優(yōu)化,在數(shù)據(jù)非活動(dòng)狀態(tài)時(shí)不更新三個(gè)強(qiáng)大的LiveData
的基礎(chǔ)功能绒极,這是我們?cè)陂_(kāi)發(fā)中設(shè)計(jì)代碼結(jié)構(gòu)的一個(gè)很好的學(xué)習(xí)案例骏令,比如可以應(yīng)用到用戶連續(xù)需點(diǎn)擊多次提交只取最后一次的業(yè)務(wù)需求中
歡迎關(guān)注Mike的簡(jiǎn)書(shū)
Android知識(shí)整理