JetPack-LiveData源碼解析

LiveData原理分析

使用方法

這里借助谷歌官方文檔來簡單說明LiveData的用法:

    class NameViewModel : ViewModel() {

        // Create a LiveData with a String
        val currentName: MutableLiveData<String> by lazy {
            MutableLiveData<String>()
        }

        // Rest of the ViewModel...
    }
    

創(chuàng)建一個LiveData對象

class NameActivity : AppCompatActivity() {

    // Use the 'by viewModels()' Kotlin property delegate
    // from the activity-ktx artifact
    private val model: NameViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Other code to setup the activity...

        // Create the observer which updates the UI.
        val nameObserver = Observer<String> { newName ->
            // Update the UI, in this case, a TextView.
            nameTextView.text = newName
        }

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.currentName.observe(this, nameObserver)

        button.setOnClickListener {
            val anotherName = "John Doe"
            model.currentName.setValue(anotherName)
        }
    }
}

調(diào)用了observe方法后唾戚,就已經(jīng)注冊了監(jiān)聽掸犬。
后面不需要在onDestroy內(nèi)部移除observer站粟,這一切都在livedata內(nèi)部做好了,非常方便。
下面來看它的邏輯是怎樣實現(xiàn)的


源碼分析

首先,它的內(nèi)部是通過一個Map存儲所有的觀察者
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();
key是observer,value是包裝類LifecycleBoundObserver均唉。

當(dāng)調(diào)用observe()方法時是晨,會將其放入mObservers中。

    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);
        // 1
        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;
        }
        // 2 
        owner.getLifecycle().addObserver(wrapper);
    }

關(guān)鍵點如上面
注釋1舔箭,先將二者放入map中罩缴,便于后面通知所有的觀察者;
注釋2處將當(dāng)前wrapper再添加進LifeCycle的觀察者中,以便在頁面生命周期變化時层扶,得到通知箫章,這部分后面再具體說明。

通過第一步镜会,觀察者已經(jīng)添加進來了檬寂,一共有兩部分:

  • 外面添加的用于觀察數(shù)據(jù)變化的observer
  • LiveData自身向LifeCycle添加的生命周期wrapper observer

下面分別看看它們是怎樣通知數(shù)據(jù)變化的。


數(shù)據(jù)變化的通知

當(dāng)調(diào)用setValue設(shè)置數(shù)據(jù)時戳表,代碼如下:

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

主要是dispatchingValue方法桶至,如果參數(shù)傳入null昼伴,則會遍歷通知所有的觀察者,否則只會通知傳入的特定觀察者

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                // 如果該參數(shù)不是null镣屹,那么只會通知這個特定的觀察者
                considerNotify(initiator);
                initiator = null;
            } else {
                // 否則圃郊,循環(huán)遍歷mObservers,逐個通知
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

這里傳入的參數(shù)是null女蜈,主要看for循環(huán)中的considerNotify方法

    private void considerNotify(ObserverWrapper observer) {
        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)是否已經(jīng)start或resume
        if (!observer.shouldBeActive()) {
            // 如果不是,則重新刷新LiveData內(nèi)部生命周期監(jiān)聽的狀態(tài)
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        // 回調(diào)onChanged方法伪窖,這里就回到我們設(shè)置的自定義監(jiān)聽了
        observer.mObserver.onChanged((T) mData);
    }

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

上面的邏輯簡單總結(jié)如下:
由于在LiveData內(nèi)部持有了LifeCycle的生命周期監(jiān)聽逸寓,在數(shù)據(jù)發(fā)生變化,要通知外部的時候惰许,先檢查生命周期的狀態(tài)席覆,如果已經(jīng)不是在start或resume,則不會發(fā)出通知汹买。否則佩伤,就回調(diào)onChanged方法


生命周期變化的監(jiān)聽

在調(diào)用LiveData.observe()方法時,其內(nèi)部同時調(diào)用了LifeCycle.addObserver()方法晦毙,傳入的參數(shù)是LiveData的內(nèi)部類LifecycleBoundObserver生巡,它實現(xiàn)了GenericLifecycleObserver接口。

主要看下它的onStateChanged()方法

@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());
}

// 該方法主要在監(jiān)測到頁面銷毀時調(diào)用见妒,移除自身作為lifeCycle的觀察者
void detachObserver() {
    mOwner.getLifecycle().removeObserver(this);
}

可以看到孤荣,當(dāng)觀察到頁面銷毀時,會調(diào)用LiveData.removeObserver去移除當(dāng)前相關(guān)聯(lián)的observer须揣,也就是我們調(diào)用LiveDta.observe()時傳入的自定義數(shù)據(jù)觀察者盐股,避免了內(nèi)存泄露產(chǎn)生。

當(dāng)不是destroy狀態(tài)耻卡,而是start或者resume時疯汁,則會去調(diào)用activeStateChanged(true),隨后卵酪,又會調(diào)用dispatchingValue(this)幌蚊,上文我們講過,當(dāng)傳入的參數(shù)不是null的時候溃卡,只會通知這一條特定的觀察者溢豆,這時一條完整的通知邏輯就出現(xiàn)了。
這里使得當(dāng)應(yīng)用從后臺切到前臺時瘸羡,數(shù)據(jù)始終可以保持是最新的狀態(tài)

    public void removeObserver(@NonNull final Observer<? super T> observer) {
        assertMainThread("removeObserver");
        // 先刪除我們傳入的observer
        ObserverWrapper removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }

        // 再將自身作為lifeCycle觀察者的身份移除
        removed.detachObserver();
        removed.activeStateChanged(false);
    }

關(guān)鍵點都在代碼中注釋出來了漩仙。
直到將兩個觀察者對象都完全移除,該觀察者的責(zé)任就走到了盡頭。


總結(jié)

  • LiveData是黏性監(jiān)聽的讯赏。在首次設(shè)置監(jiān)聽時垮兑,會遍歷所有的觀察者,做一次通知
  • LiveData通過維護2個觀察者漱挎,實現(xiàn)了數(shù)據(jù)+生命周期的監(jiān)聽系枪。一個是我們傳入的數(shù)據(jù)Observer,另一個是其自身向LifeCycle設(shè)置的生命周期監(jiān)聽
  • LiveData只會去給處于前臺(STARTED和RESUMED) 的頁面發(fā)送數(shù)據(jù)變化通知
  • 不會發(fā)生內(nèi)存泄露:類似的磕谅,在監(jiān)測到頁面Destroy時私爷,會同時移除上面兩個監(jiān)聽,避免內(nèi)存泄露
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膊夹,一起剝皮案震驚了整個濱河市衬浑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌放刨,老刑警劉巖工秩,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異进统,居然都是意外死亡助币,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門螟碎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來眉菱,“玉大人,你說我怎么就攤上這事掉分〖蠡海” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵酥郭,是天一觀的道長华坦。 經(jīng)常有香客問我,道長不从,這世上最難降的妖魔是什么惜姐? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮消返,結(jié)果婚禮上载弄,老公的妹妹穿的比我還像新娘耘拇。我一直安慰自己撵颊,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布惫叛。 她就那樣靜靜地躺著倡勇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妻熊,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天夸浅,我揣著相機與錄音,去河邊找鬼扔役。 笑死帆喇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亿胸。 我是一名探鬼主播坯钦,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侈玄!你這毒婦竟也來了婉刀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤序仙,失蹤者是張志新(化名)和其女友劉穎突颊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體潘悼,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡律秃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了挥等。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片友绝。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肝劲,靈堂內(nèi)的尸體忽然破棺而出迁客,到底是詐尸還是另有隱情,我是刑警寧澤辞槐,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布掷漱,位于F島的核電站,受9級特大地震影響榄檬,放射性物質(zhì)發(fā)生泄漏卜范。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一鹿榜、第九天 我趴在偏房一處隱蔽的房頂上張望海雪。 院中可真熱鬧,春花似錦舱殿、人聲如沸奥裸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽湾宙。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侠鳄,已是汗流浹背埠啃。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伟恶,地道東北人碴开。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像博秫,于是被迫代替她去往敵國和親叹螟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344