Jetpack源碼解析(二):LiveData解析

JetPack作為Google官方推薦的一套標準化開發(fā)套件,很值得去使用和學(xué)習(xí)酬荞。

這篇介紹LiveDataLiveData 是一種可觀察的數(shù)據(jù)存儲器類瞧哟。與常規(guī)的可觀察類不同混巧,LiveData 具有生命周期感知能力,意指它遵循其他應(yīng)用組件(如 Activity勤揩、Fragment 或 Service)的生命周期咧党。這種感知能力可確保 LiveData 僅更新處于活躍生命周期狀態(tài)的應(yīng)用組件觀察者。

基本使用

LiveData本質(zhì)是一個①遵循生命周期陨亡、②可供觀察的存儲類凿傅。

使用姿勢1 使用姿勢2
正常使用.png
黏性使用.png

乍一看和我們之前學(xué)的EventBusRxBus之類的事件總線很像数苫,不就是個觀察者模式嘛。姿勢2不就是反映出黏性的狀態(tài)嘛辨液。

事實也是如此虐急。

不過它具有生命周期感知能力LiveData 只會將更新通知給活躍的觀察者滔迈。為觀察 LiveData對象而注冊的非活躍觀察者不會收到更改通知止吁。

Google官方不建議我們這樣直接在Activity中使用,而是將LiveData放在ViewModle中燎悍,Activity中只負責(zé)監(jiān)聽及變化后的處理事件敬惦。以構(gòu)建成MVVM架構(gòu)。

源碼解析

image-20201218114150694.png

MutableLiveData繼承自LiveData谈山,直接看LiveDataobserve()方法俄删。

observe(owner,observer)

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    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);
}

之前說過,LiveData 僅更新處于活躍生命周期狀態(tài)的應(yīng)用組件觀察者奏路,這里的活躍狀態(tài)具體指的是STARTRESUME畴椰。

首先是必須要在主線程執(zhí)行,判斷生命周期狀態(tài)是否活躍鸽粉;然后new一個LifecycleBoundObserver命名為wrapper斜脂,將其作為VALUE,以傳進來的observer為KEY,存入mObservers中触机。

然后將wrapper放入生命周期的監(jiān)聽中帚戳。wrapper實現(xiàn)了LifecycleEventObserver接口玷或,所以wrapper放進去后接著就會調(diào)用wrapper中的onStateChanged方法【這一句內(nèi)容屬于Lifecycle知識點,詳見Lifecycle解析】片任。

wrapper.onStateChanged

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

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

判斷了下生命周期是否活躍偏友,然后走向了activeStateChanged( )

這里傳入的布爾值是判斷生命周期狀態(tài):當(dāng)前狀態(tài)在STARTED之前蚂踊,false; 在STARTED狀態(tài)之后约谈,true。

說明只有在STARTEDRESUME狀態(tài)時會傳true進去犁钟,印證了之前說的 “活躍狀態(tài)具體指的是STARTRESUME棱诱。

//這個方法是在抽象類`ObserverWrapper`中。
void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    // immediately set active state, so we'd never dispatch anything to inactive
    // owner
    mActive = newActive;
    boolean wasInactive = LiveData.this.mActiveCount == 0;
    LiveData.this.mActiveCount += mActive ? 1 : -1;
    if (wasInactive && mActive) {
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        onInactive();
    }
    if (mActive) {
        dispatchingValue(this);
    }
}

這個方法中涝动,先將傳進來的newActive賦值給mActive迈勋。

mActiveCount表示活躍狀態(tài)下 obs的數(shù)量;wasInactive(為true)表示之前活躍狀態(tài)下沒有obs;

mActivetrue表示當(dāng)下處于活躍狀態(tài)醋粟,并且你傳給了我個obs靡菇。

所以mActive為true的時候,mActiveCount要+1米愿;為false時厦凤,mActiveCount要-1。注意這里mActiveCount發(fā)生了改變S丁较鼓!

然后判斷,如果改變之前沒有obs活躍 && 傳來的obs即將變活躍违柏,那就要onActive();

如果 改變之后依然沒有obs活躍 && 傳來的obs也不會變活躍博烂,那就onInactive();

當(dāng)然這兩個方法都是空的,需要用戶自己去寫漱竖。

說人話就是:當(dāng)活躍的數(shù)量0-->1時禽篱,執(zhí)行onActive();由1-->0時馍惹,執(zhí)行onInactive()躺率。

最重要的其實是下一句:如果現(xiàn)在是活躍狀態(tài),我收到了obs讼积,所以要執(zhí)行下一步操作了肥照。

dispatchingValue(wrapper)

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;
}

這兩個布爾值,emmm勤众,看的有點繞舆绎。再看這個do while,這循環(huán)怎么這么怪?

//第一次A,B = false
 if(A){
     B = true;
     return
 }
A = true
do{
    B = false
        ...//不會改變B的代碼
}while(B)

WTF??一臉黑人問號们颜?這種循環(huán)能跑到第二圈吕朵?這種循環(huán)要是能循起來猎醇,我當(dāng)場把顯示器吃掉!努溃!

不管了硫嘶,重點看do while中的代碼,傳進來wrapper是不為空的梧税,走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;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
}

一些列判斷之后,如果未被return第队,則執(zhí)行wrapper.mObserver.onChanged()方法哮塞。

當(dāng)然,第一次 observer.mLastVersionmVersion都是初始值-1凳谦,所以不會執(zhí)行到此忆畅。除非mVersion變了...

大的流程走通了,再看看setValue方法尸执。

setValue

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

版本++家凯,存儲data,然后再dispatchingValue(null);如失。

等等绊诲,這里也進到了dispatchingValue,也就是說dispatchingValue中可能出現(xiàn)多個線程同時訪問進去的情況褪贵。這樣一來之前看不懂的那兩個布爾值和一個長得奇怪的do while循環(huán)就說得過去了驯镊。

Snipaste_2020-12-18_16-36-26.png

這種情況下,循環(huán)就循環(huán)起來了竭鞍。

image-20201218170327040.png

咳咳,我們繼續(xù)??橄镜。dispatchingValue方法中有一個針對null的判斷偎快,這時候傳進來的initiator為空,走else的方法洽胶。把mObservers遍歷一遍晒夹,然后依次執(zhí)行considerNotify(iterator.next().getValue());

如果大家記性不太好的話姊氓,我?guī)痛蠹一貞浺幌拢?/p>

LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

所以getValue方法中得到的就是我們在observe方法中初始化的wrapper丐怯。

再看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;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
}

這次進入這個方法的時候有一點不一樣,我們在setValue方法中版本++了翔横,所以這個時候mVersion大于mLastVersion了读跷,這個時候才會執(zhí)行observer.onChanged方法。

mVersion的意義就在這里體現(xiàn)出來了禾唁。

如果你先setValue(版本++),然后再observer()效览,onChanged依然會被觸發(fā)无切。這就是所謂的黏性事件。也就是說黏性是在源碼中寫死了的丐枉,不支持像EventBus那般可進行配置哆键。你想不黏性都不行。

postValue

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() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        setValue((T) newValue);
    }
};

postValue也一樣瘦锹,通過一個mPostValueRunnable籍嘹,最后轉(zhuǎn)到了SetValue方法。

總結(jié)

LiveData的作用其實是事件的分發(fā)弯院,其實現(xiàn)原理是基于發(fā)布/訂閱者模式辱士,但是LiveData和傳統(tǒng)的EventBus、RxBus這類事件總線又不太一樣抽兆。

Google對其在功能上加以限制识补,幾乎只有set/post和observe這三個方法;而上述Bus框架理論上可以在代碼的任意一處進行分發(fā)辫红,在代碼的其他地方進行任意調(diào)用凭涂,雖然靈活,但無形中增加了管理成本贴妻,尤其是當(dāng)項目日益龐大的時候切油,若項目中的Bus管理不善,發(fā)送和接收的事件(event)將會泛濫成災(zāi)名惩,造成不可預(yù)期的后果澎胡。

而LiveData,可以通過和ViewModle的配合娩鹉,完美規(guī)避這些問題攻谁,使事件的傳遞更加純粹 和 易于管理。

同時弯予,LiveData生命周期感知能力也避免了由于生命周期的變化帶來的內(nèi)存泄露等問題戚宦。

當(dāng)然,你也完全可以利用LiveData將其封裝成LiveDataBus锈嫩,實現(xiàn)相似的事件總線框架受楼,但是這有違Google的初衷,Google開發(fā)團隊的初衷是讓我們使用LiveData+ViewModle的模式來管理數(shù)據(jù)呼寸,這才是Google團隊認為的最佳做法艳汽。


最后,關(guān)于LiveData的粘性設(shè)計可能帶來的BUG和解決方案对雪,我會另開一篇文章詳談河狐,不在此贅述。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市甚牲,隨后出現(xiàn)的幾起案子义郑,更是在濱河造成了極大的恐慌,老刑警劉巖丈钙,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件非驮,死亡現(xiàn)場離奇詭異,居然都是意外死亡雏赦,警方通過查閱死者的電腦和手機劫笙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來星岗,“玉大人填大,你說我怎么就攤上這事∏伍伲” “怎么了允华?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寥掐。 經(jīng)常有香客問我靴寂,道長,這世上最難降的妖魔是什么召耘? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任百炬,我火速辦了婚禮,結(jié)果婚禮上污它,老公的妹妹穿的比我還像新娘剖踊。我一直安慰自己,他們只是感情好衫贬,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布德澈。 她就那樣靜靜地躺著,像睡著了一般固惯。 火紅的嫁衣襯著肌膚如雪圃验。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天缝呕,我揣著相機與錄音,去河邊找鬼斧散。 笑死供常,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸡捐。 我是一名探鬼主播栈暇,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼箍镜!你這毒婦竟也來了源祈?” 一聲冷哼從身側(cè)響起煎源,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎香缺,沒想到半個月后手销,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡图张,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年锋拖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祸轮。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡兽埃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出适袜,到底是詐尸還是另有隱情柄错,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布苦酱,位于F島的核電站售貌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躏啰。R本人自食惡果不足惜趁矾,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望给僵。 院中可真熱鬧毫捣,春花似錦、人聲如沸帝际。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹲诀。三九已至斑粱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脯爪,已是汗流浹背则北。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痕慢,地道東北人尚揣。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像掖举,于是被迫代替她去往敵國和親快骗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

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