深入理解JetPack之LiveData

LiveData是什么喇闸?

1.首先LiveData在用法上其實(shí)是與數(shù)據(jù)實(shí)體類是一樣的東西,它負(fù)責(zé)暫存數(shù)據(jù)。
2.其次LiveData其實(shí)也是一個(gè)觀察者模式的數(shù)據(jù)實(shí)體類,它可以跟它注冊(cè)的觀察者回調(diào)數(shù)據(jù)是否已經(jīng)更新,比如以前的接口回調(diào)通知可以用LiveData做到躏救。
3.LiveData還能知曉它綁定的Activity或者Fragment的生命周期,它只會(huì)給前臺(tái)活動(dòng)的組件回調(diào)偶妖,避免組件銷毀后發(fā)生意想不到的崩潰情況。

那LiveData是如何做到以上這些功能的呢纯陨?

LiveData如何是如何能觀察到組件生命周期的
從使用開(kāi)始入手源碼:
var myLiveData = MutableLiveData<Any>()

myLiveData.observe(this, Observer {

})

這里通過(guò)調(diào)用LiveData的observe()方法來(lái)注冊(cè)觀察者坛芽,LiveData的observe()方法如下:

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        //該方法調(diào)用者必須在主線程
        assertMainThread("observe");
        //如果處在DESTROYED 狀態(tài),則沒(méi)必要添加觀察者
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
       //將包裝結(jié)果添加到Map里
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);  
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

方法的第一個(gè)參數(shù)owner翼抠,就是注冊(cè)時(shí)的組件咙轩,即為activity或fragment,需要為實(shí)現(xiàn)LifecycleOwner接口阴颖。獲取組件當(dāng)前的生命周期狀態(tài)活喊,如果狀態(tài)為DESTROYED,那么直接return量愧,這就保證了DESTROYED狀態(tài)的組件是不允許注冊(cè)的钾菊。

mObservers.putIfAbsent(observer, wrapper)方法將observer和LifecycleBoundObserver存儲(chǔ)到SafeIterableMap類型的mObservers中,putIfAbsent方法和put方法有區(qū)別偎肃,如果傳入key對(duì)應(yīng)的value已經(jīng)存在煞烫,就返回存在的value,不進(jìn)行替換累颂。如果不存在滞详,就添加key和value,返回null。

最后的owner.getLifecycle().addObserver(wrapper)料饥,就在LiveData內(nèi)部完成了Lifecycle的觀察者的添加蒲犬,這樣LiveData就有了觀察組件生命周期變化的能力。

LiveData是如何回調(diào)observe方法的

    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

當(dāng)宿主(Activity/Fragment) 生命周期發(fā)生改變時(shí)會(huì)調(diào)用onStateChanged()方法稀火,并且判斷了當(dāng)組件處于DESTROYED狀態(tài)時(shí)暖哨,會(huì)調(diào)用removeObserver方法,來(lái)移除observer凰狞。所以當(dāng)組件銷毀時(shí)篇裁,注冊(cè)的生命周周觀察者不會(huì)再接收到通知,因?yàn)橐呀?jīng)解綁了赡若。

shouldBeActive()用來(lái)判斷當(dāng)前宿主是否是活躍狀態(tài)达布,此處定義的活躍狀態(tài)為:宿主的狀態(tài)要>="STARTED"狀態(tài),而該狀態(tài)區(qū)間為:Activity.onStart() 之后且Activity.onPause()之前逾冬。
當(dāng)宿主處于活躍狀態(tài)時(shí)黍聂,才會(huì)繼續(xù)通知UI 數(shù)據(jù)變更了,進(jìn)而刷新UI身腻,若是處于非活躍狀態(tài)产还,比如App 失去焦點(diǎn)(onPause()被調(diào)用),那么將不會(huì)刷新UI 嘀趟。

通知觀察者
  • Observer的回調(diào)事件通過(guò)setValue和postValue觸發(fā)脐区。

setValue方法:

   @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

從代碼中方法注解可以看出,setValue方法必須運(yùn)行在主線程中她按,其內(nèi)部調(diào)用了dispatchingValue方法牛隅,其后將事件通知回調(diào)給observers。

再看dispatchingValue方法:

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                //遍歷調(diào)用所有觀察者
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

在分發(fā)有效的前提下酌泰,遍歷mObservers媒佣,一次調(diào)用considerNotify()方法。

    private void considerNotify(ObserverWrapper observer) {
//非活躍狀態(tài)陵刹,直接返回
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
       //如果LiveData數(shù)據(jù)版本<= 觀察者的數(shù)據(jù)版本默伍,則直接返回
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }

如果當(dāng)前observer的mActive值不為true,就直接return衰琐。如果判斷條件都滿足也糊,則調(diào)用Observer接口的onChanged()方法,這個(gè)方法正是開(kāi)頭myLiveData.observe(this, Observer {}) Observer接口的方法碘耳,因此完成liveData變化對(duì)observer回調(diào)事件的輪回显设。

public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(T t);
}
postValue()
     protected void postValue(T value) {
        boolean postTask;
        //子線程、主線程都需要修改mPendingData辛辨,因此需要加鎖
        synchronized (mDataLock) {
            //mPendingData 是否還在排隊(duì)等待發(fā)送出去
            //mPendingData == NOT_SET 表示當(dāng)前沒(méi)有排隊(duì)
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            //說(shuō)明上次的Runnable 還沒(méi)執(zhí)行
            //直接返回捕捂,不需要切換到主線程執(zhí)行
            return;
        }
        //切換到主線程執(zhí)行Runnable
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

    private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
          // 切換到主線程執(zhí)行Runnable
            setValue((T) newValue);
        }
    };

而postValue方法里瑟枫,通過(guò)任務(wù)(Runnable)的方式在主線程中更新數(shù)據(jù)。而getValue中指攒,如果livedata中mData設(shè)過(guò)值就返回mData慷妙,否則會(huì)返回null,這里在實(shí)際使用中可能會(huì)造成空指針異常允悦。

livedata為什么連續(xù)post會(huì)丟失數(shù)據(jù)?

我們實(shí)際場(chǎng)景更多的是在子線程發(fā)起的膝擂,所以會(huì)用postValue來(lái)通知數(shù)據(jù)變化。
postValue里首先好會(huì)將分發(fā)的值賦值給mPendingData全局變量隙弛,然后將值的分發(fā)操作放在一個(gè)Runnable里進(jìn)行架馋,從postValue到 ArchTaskExecutor執(zhí)行postToMainThread方法(其實(shí)里面就是mMainHandler.post(runnable))是有一個(gè)時(shí)間間隙的。
在這個(gè)時(shí)間間隙中全闷,由于連續(xù)兩次postValue,所以mPendingData帶著第二次的最新值叉寂,進(jìn)行分發(fā),所以UI刷新总珠,只能看到第二次的值屏鳍。
其實(shí)剛才分析過(guò)程中我們也看到了,由于第二次執(zhí)行postValue的時(shí)候局服,mPendingData已經(jīng)不是初始值钓瞭,所以導(dǎo)致postTask為false,在3處淫奔,執(zhí)行了return山涡。所以雖然執(zhí)行了兩次postValue,可是ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable)只會(huì)執(zhí)行一次搏讶。

總結(jié)一下LiveData運(yùn)行流程:

1.通過(guò)調(diào)用LiveData的observe()方法來(lái)注冊(cè)觀察者佳鳖,獲取組件當(dāng)前的生命周期狀態(tài)霍殴,如果狀態(tài)為DESTROYED媒惕,那么直接return。owner.getLifecycle().addObserver(wrapper)来庭,就在LiveData內(nèi)部完成了Lifecycle的觀察者的添加妒蔚,這樣LiveData就有了觀察組件生命周期變化的能力。

2.當(dāng)組件狀態(tài)發(fā)生變化時(shí)月弛,會(huì)調(diào)用onStateChanged方法肴盏,并且判斷了當(dāng)組件處于DESTROYED狀態(tài)時(shí),會(huì)調(diào)用removeObserver方法來(lái)移除observer帽衙。具體事件分發(fā)是通過(guò)ObserverWrapper菜皂,它是Observer的包裝類,如果是Active狀態(tài)厉萝,會(huì)調(diào)用dispatchingValue方法恍飘,并將自身傳進(jìn)去榨崩。

3.在分發(fā)有效的前提下,遍歷mObservers章母,如果當(dāng)前observer的mActive值不為true母蛛,就直接return。如果判斷條件都滿足乳怎,則調(diào)用Observer接口的onChanged()方法彩郊。

4.最后,通過(guò)setValue和postValue方法內(nèi)部調(diào)用dispatchingValue方法蚪缀,其后將事件通知回調(diào)給observers秫逝。

粘性事件

LiveData數(shù)據(jù)變更發(fā)生后,才注冊(cè)的觀察者询枚,此時(shí)觀察者還能收到變更通知筷登。

這和LiveData 的實(shí)現(xiàn)有關(guān),看看核心源碼實(shí)現(xiàn):

 private void considerNotify(LiveData.ObserverWrapper observer) {
        //mVersion 為L(zhǎng)iveData 當(dāng)前數(shù)據(jù)版本哩盲,當(dāng)setValue/postValue 發(fā)生時(shí)前方,mVersion++
        //通過(guò)比對(duì)LiveData 當(dāng)前數(shù)據(jù)版本與觀察者的數(shù)據(jù)版本,若是發(fā)現(xiàn)LiveData 當(dāng)前數(shù)據(jù)版本 更大
        //說(shuō)明是之前沒(méi)有通知過(guò)觀察者廉油,因此需要通知惠险,反之則不通知。
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        //將觀察者數(shù)據(jù)版本保持與LiveData 版本一致抒线,表明該觀察者消費(fèi)了最新的數(shù)據(jù)班巩。
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

再回溯一下流程:

1、初始時(shí)LiveData.mVersion= -1嘶炭,ObserverWrapper.mLastVersion = -1抱慌,因此初次進(jìn)入Activity時(shí)沒(méi)有數(shù)據(jù)通知。
2眨猎、當(dāng)點(diǎn)擊按鈕后(LiveData.setValue())抑进,此時(shí)LiveData.mVersion = 0;因?yàn)長(zhǎng)iveData.mVersion>ObserverWrapper.mLastVersion睡陪,因此觀察者能夠收到通知寺渗。
3、當(dāng)退出Activity 再進(jìn)來(lái)后兰迫,因?yàn)镺bserverWrapper 是全新new 出來(lái)的信殊,ObserverWrapper.mLastVersion = -1,而LiveData.mVersion =0汁果,還是大于 ObserverWrapper.mLastVersion涡拘,因此依然能夠收到通知。

參考:
https://blog.csdn.net/jwg1988/article/details/122925810
https://juejin.cn/post/7081841015643963406

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末据德,一起剝皮案震驚了整個(gè)濱河市鳄乏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汞窗,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仲吏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡裹唆,警方通過(guò)查閱死者的電腦和手機(jī)誓斥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門劳坑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人成畦,你說(shuō)我怎么就攤上這事⊙剩” “怎么了框仔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)拄养。 經(jīng)常有香客問(wèn)我离斩,道長(zhǎng),這世上最難降的妖魔是什么瘪匿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任跛梗,我火速辦了婚禮,結(jié)果婚禮上棋弥,老公的妹妹穿的比我還像新娘核偿。我一直安慰自己,他們只是感情好嘁锯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布宪祥。 她就那樣靜靜地躺著聂薪,像睡著了一般家乘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上藏澳,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天仁锯,我揣著相機(jī)與錄音,去河邊找鬼翔悠。 笑死业崖,一個(gè)胖子當(dāng)著我的面吹牛野芒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播双炕,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狞悲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了妇斤?” 一聲冷哼從身側(cè)響起摇锋,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎站超,沒(méi)想到半個(gè)月后荸恕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡死相,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年融求,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片算撮。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡生宛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肮柜,到底是詐尸還是另有隱情茅糜,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布素挽,位于F島的核電站蔑赘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏预明。R本人自食惡果不足惜缩赛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一撰糠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旨袒,春花似錦术辐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兴想。三九已至嫂便,卻和暖如春闸与,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背几迄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工映胁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坑填。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓脐瑰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親苍在。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荠商,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354