LiveData為何這么香哩簿,這些你都知道嗎?

前言

大家好酝静,我是小益节榜!使用Jetpack全家桶的同學(xué)相信對LiveData都不陌生,其可以在很大程度上避免內(nèi)存泄漏别智、更好的將View層與Model層分離等諸多優(yōu)點使得LiveData在MVVM模式中開發(fā)成為不可或缺的利器宗苍。今天我們就將LiveData剖開來瞧瞧,為何它這么香薄榛!

推薦

文章將率先在公眾號「碼途有道」上發(fā)布讳窟,歡迎大家關(guān)注!

一敞恋、LiveData的常用方法

LiveData主要是基于觀察者機制實現(xiàn)的丽啡,LiveData是被觀察者,其觀察者是Observer硬猫,當(dāng)LiveData有數(shù)據(jù)變動時會通知它的觀察者去更新數(shù)據(jù)补箍。LiveData常用的幾個方法如下:

  • setVaule
  • postValue
  • observe
  • observeForever

二、setValue

setVaulepostVaule都是用來修改LiveData的值的啸蜜,我們先看setValue的源碼:

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

在上述代碼中有一個@MainThread注解坑雅,表示setValue只能在主線程中調(diào)用,并且在方法開頭還有一句assertMainThread("setValue")來判斷當(dāng)前調(diào)用是否是在主線程中進行衬横,如果不是在主線程則會拋出異常裹粤。

mVersion++用于記錄LiveData是否有更新,每有一次更新蜂林,mVersion都會加一蛹尝,在后續(xù)會使用到后豫。

mData = value非常好理解,只是簡單的記錄需要更新的值突那。

dispatchingValue(null)用于通知觀察者更新數(shù)據(jù)挫酿,源碼如下:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, 
            ObserverWrapper>> iterator = mObservers.iteratorWithAdditions();
            iterator.hasNext();
            ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

dispatchingValue(@Nullable ObserverWrapper initiator)傳入的參數(shù)是觀察者,如果有觀察者傳入愕难,只通知這個傳入的觀察者進行數(shù)據(jù)更新早龟,否則會遍歷該LiveData下所有的觀察者,并一一通知更新猫缭。在上述代碼中真正去通知觀察者進行數(shù)據(jù)更新的是considerNotify()方法葱弟,源碼如下:

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

源碼中的流程非常簡單:

  • 首先通過觀察者的mActive屬性判斷是否處于活躍狀態(tài);如果活躍繼續(xù)往下走猜丹,否則到此結(jié)束芝加。
  • 之后通過觀察者的shouldBeActive()方法判斷其所在頁面是否處于活躍狀態(tài)(比如觀察者所在的頁面已經(jīng)走到onDestory函數(shù),那么頁面就不是處于活躍狀態(tài)),如不處于活躍狀態(tài)則需要將觀察者自身的狀態(tài)修改為非活躍狀態(tài)射窒。
  • 如處于活躍狀態(tài)藏杖,再通過將觀察者自己的mLastVersion與LiveData的mVersion進行對比,若mLastVersion >= mVersion則代表沒有更新脉顿,否則就是有更新蝌麸。
  • 最后,若有更新艾疟,則觀察者會修改自己的mLastVersion并調(diào)用onChanged()方法做數(shù)據(jù)更新處理来吩。

三、postValue

static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET;

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};

上面是postValue的相關(guān)源碼蔽莱,我們先理一下邏輯:mPendingData的初始值是NOT_SET弟疆,每當(dāng)有更新時,mPendingData會記錄新的value盗冷,之后post一個Runnable交給主線程處理兽间,Runnable內(nèi)部會調(diào)用setValue來更新數(shù)據(jù),并且將mPendingData的值重新置為NOT_SET正塌。

postValuesetValue相比,postValue可以在任意線程中調(diào)用恤溶,而setValue只能在主線程中調(diào)用乓诽,但是postValue內(nèi)部最終還是通過調(diào)用setValue來實現(xiàn)數(shù)據(jù)更新。

postValue有一點需要注意:多次調(diào)用postValue咒程,只有最后一次調(diào)用時傳入的值有效鸠天。因為在postValue源碼中,是通過mPendingData來記錄需要更新的值帐姻,并通過mPendingData == NOT_SET來決定是否需要更新稠集;但在更新未結(jié)束前奶段,mPendingData不等于NOT_SET,即不能提交新的Runnable剥纷,而mPendingData的值卻可以被更新痹籍,所以更新時獲取的就是最后一次傳入的值。

四晦鞋、observer.shouldBeActive()

在上述提到的considerNotify()方法中蹲缠,有一個shouldBeActive()方法,此方法屬于觀察者悠垛,用于判斷觀察者所在頁面是否處于活躍狀態(tài)线定。而在LiveData中,共有兩種觀察者:LifecycleBoundObserverAlwaysActiveObserver确买。

4.1 LifecycleBoundObserver與observe

LifecycleBoundObserver是帶有對所在頁面生命周期判斷的觀察者斤讥,與LiveData的observe()方法聯(lián)用(observe()內(nèi)部調(diào)用的是LifecycleBoundObserver),源碼如下:

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    ......
    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
    ......
}

LifecycleBoundObservershouldBeActive()方法中湾趾,會判斷頁面至少要處于STARTED狀態(tài)才認為是處于活躍狀態(tài)芭商,即當(dāng)Activity處于onStart()onPause()之間則認為處于活躍狀態(tài)。以下是狀態(tài)源碼:

public enum State {
        // onDestroy()
        DESTROYED,
        INITIALIZED,
        // 介于 onCreate() -- onStop() 之間
        CREATED,
        // 介于 onStart() -- onPause() 之間
        STARTED,
        // onResume()
        RESUMED;
    public boolean isAtLeast(@NonNull State state) {
    }
}

綜上可得撑帖,正因為LifecycleBoundObservershouldBeActive()方法的實現(xiàn)蓉坎,才使得LiveData具有可以避免內(nèi)存泄漏的能力

4.2 AlwaysActiveObserver與observeForever

AlwaysActiveObserver作為LiveData中的另一種觀察者胡嘿,與observeForever()方法聯(lián)用(observeForever()內(nèi)部調(diào)用的是AlwaysActiveObserver)蛉艾,其shouldBeActive()直接返回true,源碼如下:

private class AlwaysActiveObserver extends ObserverWrapper {
    ......
    @Override
    boolean shouldBeActive() {
        return true;
    }
}

如此一來衷敌,使用observeForever()方法進行訂閱后勿侯,便沒有了對當(dāng)前頁面進行生命周期判斷的能力;每當(dāng)有數(shù)據(jù)變動時缴罗,都會通知觀察者進行更新處理助琐。使用此方法,需要注意內(nèi)存泄漏問題面氓。

五兵钮、小結(jié)

本章內(nèi)容主要講解LiveData的內(nèi)部觀察者機制,讓大家使用時更加了然于心舌界。LiveData除了作為MVVM模式中的重要一環(huán)之外掘譬,現(xiàn)在很多開發(fā)者還將其用作應(yīng)用內(nèi)的通信手段,后續(xù)文章我們將會介紹如何使用LiveData搭建通訊框架呻拌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葱轩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌靴拱,老刑警劉巖垃喊,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異袜炕,居然都是意外死亡本谜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門妇蛀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕突,“玉大人,你說我怎么就攤上這事评架【熳拢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵纵诞,是天一觀的道長上祈。 經(jīng)常有香客問我,道長浙芙,這世上最難降的妖魔是什么登刺? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮嗡呼,結(jié)果婚禮上纸俭,老公的妹妹穿的比我還像新娘。我一直安慰自己南窗,他們只是感情好揍很,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著万伤,像睡著了一般窒悔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敌买,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天简珠,我揣著相機與錄音,去河邊找鬼虹钮。 笑死聋庵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芙粱。 我是一名探鬼主播祭玉,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宅倒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拐迁,失蹤者是張志新(化名)和其女友劉穎蹭劈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體线召,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡铺韧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缓淹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哈打。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖讯壶,靈堂內(nèi)的尸體忽然破棺而出料仗,到底是詐尸還是另有隱情,我是刑警寧澤伏蚊,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布立轧,位于F島的核電站,受9級特大地震影響躏吊,放射性物質(zhì)發(fā)生泄漏氛改。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一比伏、第九天 我趴在偏房一處隱蔽的房頂上張望胜卤。 院中可真熱鬧,春花似錦赁项、人聲如沸葛躏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽紫新。三九已至,卻和暖如春李剖,著一層夾襖步出監(jiān)牢的瞬間芒率,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工篙顺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偶芍,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓德玫,卻偏偏與公主長得像裕照,于是被迫代替她去往敵國和親殖蚕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容