JetPack 之 LiveData

Q:什么是LiveData?

LiveData 是一種可觀察的數據存儲器類。與常規的可觀察類不同,LiveData 具有生命周期感知能力,意指它遵循其他應用組件(如 Activity、Fragment 或 Service)的生命周期。這種感知能力可確保 LiveData 僅更新處于活躍生命周期狀態的應用組件觀察者。

Q:請談談LiveData的好處

1.確保界面符合數據狀態
LiveData 遵循觀察者模式。當底層數據發生變化時,LiveData 會通知 Observer 對象。您可以整合代碼以在這些 Observer 對象中更新界面。這樣一來,您無需在每次應用數據發生變化時更新界面,因為觀察者會替您完成更新。
2.不會發生內存泄露
觀察者會綁定到 Lifecycle 對象,并在其關聯的生命周期遭到銷毀后進行自我清理。
3.不會因 Activity 停止而導致崩潰
如果觀察者的生命周期處于非活躍狀態(如返回棧中的 Activity),則它不會接收任何 LiveData 事件。
4.不再需要手動處理生命周期
界面組件只是觀察相關數據,不會停止或恢復觀察。LiveData 將自動管理所有這些操作,因為它在觀察時可以感知相關的生命周期狀態變化。
5.數據始終保持最新狀態
如果生命周期變為非活躍狀態,它會在再次變為活躍狀態時接收最新的數據。例如,曾經在后臺的 Activity 會在返回前臺后立即接收最新的數據。
6.適當的配置更改
如果由于配置更改(如設備旋轉)而重新創建了 Activity 或 Fragment,它會立即接收最新的可用數據。
7.共享資源
您可以使用單例模式擴展 LiveData 對象以封裝系統服務,以便在應用中共享它們。LiveData 對象連接到系統服務一次,然后需要相應資源的任何觀察者只需觀察 LiveData 對象。

Q:LiveData為什么可以自動取消訂閱,如何避免內存泄漏?

調用 observe 方法時,會調用 owner.getLifecycle().addObserver 以達到感知生命周期的目的。

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

其中的觀察者是owner和observer的包裝對象LifecycleBoundObserver

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

當Lifecycles的State發生變化會回調onStateChanged方法,當State為DESTROYED時,則移除觀察者Observer。里面調用的是LiveData的removeObserver方法。

public void removeObserver(@NonNull final Observer<? super T> observer) {
        assertMainThread("removeObserver");
        ObserverWrapper removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }
        removed.detachObserver();
        removed.activeStateChanged(false);
    }

當頁面銷毀時,在mObservers中remove了observer,就這樣完成了訂閱的自動取消。

Q:LiveData傳相同的值會不會執行onchanged回調?

給LiveData傳值有兩種方式setValue()postValue(),它們之間的區別在于前者只能在主線程使用,后者可以在任意線程中調用,傳入的數據會暫存為mPendingData,最終會使用Handler切換回主線程中調用setValue(mPendingData)進行數據更新。

注意的是,postValue()被多次調用時,暫存數據mPendingData會被postValue()傳入的數據覆蓋,最終數據為最后一次的數據。而postValue()發起的主線程任務,在執行到之前,只會存在一個任務。

查看setValue方法:

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

注意mVersion的值,查看dispatchingValue方法:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        ....
        considerNotify(initiator);
        ....
    }

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.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

只要mVersion是大于等于之前的值,就會回調onChanged方法,也就是說,不管值是否相同,只看version的值。

Q:談談你對observeForever的認識?

// LiveData.java
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    // 創建AlwaysActiveObserver
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        // 如果existing是LiveData.LifecycleBoundObserver類的實例,拋出異常
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    // 調用AlwaysActiveObserver的activeStateChanged方法,并且傳入true
    wrapper.activeStateChanged(true);
}

再看下AlwaysActiveObserver的代碼:

// LiveData.java
private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer<? super T> observer) {
        super(observer);
    }

    // 重寫了shouldBeActive方法,并且返回true,根據上面的代碼分析可知,這個方法是用來判斷是否為活躍狀態,這里一直返回true,也就是說一直保持著活躍狀態
    @Override
    boolean shouldBeActive() {
        return true;
    }
}

observeForever是用于將指定的觀察者添加到觀察列表中,類似于調用observer方法,但是給定的LifecycleOwner狀態總是為活躍狀態,這意味著觀察者將永遠接收所有的事件,所以如果要停止觀察這個LiveData,就要手動調用removeObserver方法。

Q:PostValue收不到數據變更的通知的問題是否遇到過?或者說使用PostValue需要注意的問題
當連續調用 postValue 時,有可能只會收到最后一次數據更新通知。

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

mPendingData 被成功賦值 value 后,post 了一個 Runnable
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 將數據存入 mPendingData,mPostValueRunnable 在UI線程消費mPendingData。
  • 在 Runnable 中 mPendingData 值還沒有被消費之前,即使連續 postValue , 也不會 post 新的 Runnable
  • mPendingData 的生產 (賦值) 和消費(賦 NOT_SET) 需要加鎖
    簡單的說是因為:

postValue 只是把傳進來的數據先存到 mPendingData,然后往主線程拋一個 Runnable,在這個 Runnable 里面再調用 setValue 來把存起來的值真正設置上去,并回調觀察者們。而如果在這個 Runnable 執行前多次 postValue,其實只是改變暫存的值 mPendingData,并不會再次拋另一個 Runnable。這就會出現后設置的值把前面的值覆蓋掉的問題,會導致事件丟失。

Q:為什么PostValue要設計為只post一次Runnable?

即使 post 多次也沒有意義,所以只 post 一次即可。

對于 setValue 來說,連續調用多次,數據會依次更新:
如下,訂閱方一次收到 a b 的通知:

liveData.setValue("a");
liveData.setValue("b");

通過源碼可知,dispatchingValue()中同步調用 Observer#onChanged(),依次通知訂閱方:

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

但對于 postValue,如果當 value 變化時,我們立即post,而不進行阻塞

protected void postValue(T value) {
    mPendingData = value;
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    public void run() {
        setValue((T) mPendingData);
    }
};

由于線程切換的開銷,連續調用 postValue,收到通知只能是b、b,無法收到a。

因此,post 多次已無意義,一次即可。

Q: 為什么要加讀寫鎖?

是否 post 取決于對 mPendingData 的判斷(是否為 NOT_SET)。因為要在多線程環境中訪問 mPendingData ,不加讀寫鎖無法保證其線程安全。

protected void postValue(T value) {
    boolean postTask = mPendingData == NOT_SET; // --1
    mPendingData = value; // --2
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    public void run() {
        Object newValue = mPendingData;
        mPendingData = NOT_SET; // --3
        setValue((T) newValue);
    }
};

如上,如果在 1 和 2 之間,執行了 3,則 2 中設置的值將無法得到更新。

Q:如何解決或防止LiveData或者MutableLiveData多次回調的問題
利用SingleLiveEvent 使 observe#LiveData時只相應一次onChanged操作

public class SingleLiveEvent<T> extends MutableLiveData<T> {

    private static final String TAG = "SingleLiveEvent";

    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @MainThread
    public void observe(LifecycleOwner owner, final Observer<T> observer) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }

        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}

1.SingleLiveEvent 利用 AtomicBoolean (默認為false)進行賦值,當LiveData 進行 setValue時改變 AtomicBoolean的值(set(true)

2 使用 AtomicBoolean.compareAndSet(true,false)方法,先進行判斷(此時的AtomicBoolean的值為true)與 compareAndSet設置的except值(第一個參數)比較,因為相等所以將第二個參數設置為AtomicBoolean值設為false函數并返回 true

  1. 當再次進入該頁面雖然 LiveData值并沒有改變,仍然觸發了 observer方法,由于 AtomicBoolean已經為 false ,但是 except值為 true,與if 進行判斷所以 并不會繼續觸發 onChanged(T)方法

即只有在 setValue時相應一次onChanged(T)方法。

Android消息總線的演進之路:用LiveDataBus替代RxBus、EventBus

ViewModel的左膀右臂 數據驅動真的香

ViewModel之外的LiveData-使用Transformations和MediatorLiveData的反應模式

Android面試:說一下 LiveData 的 postValue ?與SetValue有什么區別?連續調用會有什么問題?為什么?

帶有SnackBar、Navigation和其他事件的LiveData

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容