LiveData數(shù)據(jù)通知解析以及死鎖問題處理

前言

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

img.jpg

問題場景

在我們的代碼案例中,我們點擊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 FragmentUI造成白屏的現(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ù)中有兩個重要的變量,mDispatchingValuemDispatchInvalidated,并且這兩個字段只有在這個函數(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知識整理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容