一點點入坑JetPack:LiveData篇

前言

結束了Lifecycle篇和ViewModel篇,終於到了相對複雜的LiveData篇了。java

最開始瞭解LiveData我是拒絕的,由於你不能上來就讓我用,立刻就用。第一我要試一下,我不原意用完之後...duang、duang都是bug....android

一點點入坑JetPack:ViewModel篇數據結構

一點點入坑JetPack:Lifecycle篇app

一點點入坑JetPack:LiveData篇ide

一點點入坑JetPack:實戰前戲NetworkBoundResource篇函數

一點點入坑JetPack(終章):實戰MVVM源碼分析

後來用完以後,好嗨呦,感受人生已經達到了高潮...post

正文

固然不想聽我瞎bb的,能夠直接官方文檔。若是想圖個樂,順便了解了解新技術。那歡迎光臨紅...,男賓一位,裏邊請!ui

1、概況

官網:LiveData是一個可觀察的數據持有者類。與常規observable不一樣,LiveData是生命週期感知的。this

從官方文檔上咱們能夠看到倆個關鍵詞:可觀察生命週期感知。簡單來講,Google給咱們提供了一個能夠被觀察的,而且擁有生命週期感知能力的類。那有什麼用呢?

直接上demo:

2、入門

1.一、初級官方demo

class NameViewModel : ViewModel() {
    // 這裏new了一個MutableLiveData,它是LiveData的實現類,LiveData是抽象的,很明顯不能被new
    val currentName: LiveData<String> by lazy {
        MutableLiveData<String>()
    }
}

class NameActivity : AppCompatActivity() {
    private lateinit var mModel: NameViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mModel = ViewModelProviders.of(this).get(NameViewModel::class.java)
        // 這個this是LifecycleOwner
        mModel.currentName.observe(this, Observer { newName ->
            // mNameTextView一個TextView
            mNameTextView.text = newName
        })
        // 更新被觀察者數據,LiveData會通知觀察者
        // (這裏爲了代碼簡潔直接在LiveData上post,實際這麼寫ide就直接報錯了,由於currentName返回的是LiveData,這是一個抽象類型,它裏邊的方法是protect。正常若是咱們想讓外部post返回實現類(好比:內部有一個MutableLiveData)便可。感謝評論區只出)
        mModel.currentName.postValue("MDove")
    }
}
複製代碼

若是看到這幾行代碼,豁然開朗,那麼能夠跳過看下面的部分。若是感受有些疑惑,不着急我們不打針不吃藥,坐下就是和你嘮...

1.二、demo解釋

最開始咱們先聲明瞭一個NameViewModel的ViewModel,這部份內容在ViewModel篇有所說起。內部有一個MutableLiveData的成員變量。說白了就是一個LiveData類型的String,咱們使用時是藉助LiveData的特性,但本質仍是用String。

也就是說這裏若是咱們要用一個List,那麼此時就是MutableLiveData<List<String>>()

Activity之中,咱們先獲取ViewModel,而後mModel.currentName.observe(...,...),這裏咱們就是在觀察LiveData。咱們只須要在回調中處理咱們本身的UI操做便可了。也就是demo中的mNameTextView.text = newName

LiveData會在每一次postValue(...)或者value=...時,observe()便會回調,哪怕是null。

注意

這裏有倆個點須要特別注意:

  • 一、LiveData是生命週期感知的,在當前的LifecycleOwner不處於活動狀態(例如onPasue()onStop())時,LiveData是不會回調observe()的,由於沒有意義。
  • 二、若是LiveData沒有被observe(),那麼此時你調用這個LiveData的postValue(...)/value=...,是沒有任何做用。這個咱們能夠在源碼中看到。

1.三、不一樣的LiveData實現類(系統實現)

MutableLiveData

上文我們已經見過了,沒什麼特別的,就是LiveData的實現類。就至關於List和ArrayList的關係。

MediatorLiveData

MutableLiveData的子類,它是一個比較強大的LiveData,咱們的map()switchMap()都是基於它進行實現的。 最大的特色是能夠同時監聽多個LiveData。

3、進階

官網的這個小破demo,屬實太寒酸了,你卻是加點特技啊?就這中初級用法,誰能以爲好用呢!因此,若是對LiveData稍稍有點感受,那我們不要停,一塊兒決戰到天明。

3.一、map()

初用過RxJava的小夥伴,估計會和我同樣,被各類「姿式」的操做符所正經,好比經常使用的:map、flatMap...而LiveData中一樣有這樣的操做。

一個很常見的場景:咱們經過一個惟一id去查詢這個id下的實體類,而且要同時展現兩者的數據。很簡單的業務邏輯,在LiveData中的展現是這樣的:

3.1.一、使用

class NameViewModel : ViewModel() {
    val userIdLiveData = MutableLiveData<Long>()
    // 僞碼:當userIdLiveData發生變化時,userLiveData中的map就會調用,那麼咱們就能夠獲得罪行的id
    val userLiveData: LiveData<User> = Transformations.map(userIdLiveData) { id->
        // 經過id拿到User,return一個User的實例user
        user
    }
}

// Activity中
mModel.userLiveData.observe(this, Observer { user ->
    // user變化後通知mNameTextView更新UI
    mNameTextView.text = user.name
})
// 給userIdLiveData設置id爲1
mModel.userIdLiveData.postValue("1")
複製代碼

針對這個業務場景,咱們只須要監聽咱們用戶通知UI變化的LiveData(userLiveData),而後經過userIdLiveData.postValue("1")來驅動數據的變化。

這裏可能和咱們傳統的MVP的思想並不相同,畢竟MVVM和MVP仍是有區別的,而MVVM的這種方式被稱之爲:數據驅動

3.1.二、map()源碼

咱們直接點到Transformations.map()中。

@MainThread
public static <X, Y> LiveData<Y> map(
        @NonNull LiveData<X> source,
        @NonNull final Function<X, Y> mapFunction) {
    final MediatorLiveData<Y> result = new MediatorLiveData<>();
    result.addSource(source, new Observer<X>() {
        @Override
        public void onChanged(@Nullable X x) {
            result.setValue(mapFunction.apply(x));
        }
    });
    return result;
}
複製代碼

很簡單,就是使用了MediatorLiveData,而後經過一個高階函數,將高階函數返回的內容,set到LiveData上,完成map()。

既然提到了MediatorLiveData,以及它的addSource()的方法,那麼咱們就來看看它的源碼。

3.1.三、MediatorLiveData源碼

這部分沒啥意思,能夠直接跳過看3.1.四、map()源碼總結...

進入MediatorLiveData之中,咱們會發現代碼比較少。這裏抽出倆塊比較重點的內容,咱們一同來感覺一下:

@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
    Source<S> e = new Source<>(source, onChanged);
    Source<?> existing = mSources.putIfAbsent(source, e);
    if (existing != null && existing.mObserver != onChanged) {
        throw new IllegalArgumentException(
                "This source was already added with the different observer");
    }
    if (existing != null) {
        return;
    }
    if (hasActiveObservers()) {
        e.plug();
    }
}
複製代碼

從這段代碼中,咱們粗略能夠獲得一個信息,這裏把咱們的LiveData和Observer封裝成了Source對象,而且這個對象,不能重複添加。

此外,Source的plug()方法,被調用。接下來咱們去看一看這個內部類Source的實現:

private static class Source<V> implements Observer<V> {
    final LiveData<V> mLiveData;
    final Observer<? super V> mObserver;
    int mVersion = START_VERSION;

    Source(LiveData<V> liveData, final Observer<? super V> observer) {
        mLiveData = liveData;
        mObserver = observer;
    }

    void plug() {
        mLiveData.observeForever(this);
    }

    void unplug() {
        mLiveData.removeObserver(this);
    }

    @Override
    public void onChanged(@Nullable V v) {
        if (mVersion != mLiveData.getVersion()) {
            mVersion = mLiveData.getVersion();
            mObserver.onChanged(v);
        }
    }
}
複製代碼

首先Source是一個觀察者,能夠看到,咱們外部使用的Observer會以Source的成員變量的形式,添加到傳入的LiveData中。值得注意的是,這裏使用了mLiveData.observeForever(this);

observeForever()用法能夠看到,咱們並無傳遞LifecycleOwner,所以它並不具有生命感知能力。從註釋中也可見一斑:This means that the given observer will receive all events and will never be automatically removed.

3.1.四、map()源碼總結

打住,打住吧。其實不必繼續看了。一句話總結:map()的原理就是基於MediatorLiveData,MediatorLiveData內部會將傳遞進來的LiveData和Observer封裝成內部類,而後放在內部維護的一個Map中。而且自動幫咱們完成observeForever()和removeObserver()

3.二、switchMap()

3.2.一、使用

switchMap()的場景能夠應用在切換LiveData上。這話怎麼解釋呢?

很常見的業務場景:好比你的業務用的是map(),map()中使用你本身寫的絡,並且LiveData運行的很良好,抽着煙喝着酒,啥事都沒有...就好比,上面map()那樣的代碼:

val userLiveData: LiveData<User> = Transformations.map(userIdLiveData) { id->
    // 本身的一段邏輯
    user
}
// Activity中
mViewModel.userLiveData.observe(this,Observer{->user
    //更新UI
})
複製代碼

忽然有一天,這個地方數據結構、UI都沒變,惟獨變了邏輯。此時你一個同事寫好了一個方法,讓你替換一下就行了了。不過當你調用的時候忽然返現,這個方法返回一個LiveData對象!

固然此時咱們可讓UI從新observe()這個LiveData對象:

val otherLiveData:LiveData<User> = // 同事的邏輯

// Activity中從新observe()
mViewModel.otherLiveData.observe(this,Observer{->user
    //更新UI
})
複製代碼

但是這樣的話,本身以前寫的東西不都白費了麼?因此此時,咱們可使用switchMap(),咱們只須要不多的改動,就能夠設配此次需求的變更:

val userLiveData: LiveData<User> = Transformations.switchMap(userIdLiveData) { id->
    // 直接把同事的代碼放在這裏便可
}
複製代碼

3.2.二、switchMap()源碼

有了上邊map()源碼基礎,咱們能夠很容易的看出switchMap()的端倪:

@MainThread
public static <X, Y> LiveData<Y> switchMap(
        @NonNull LiveData<X> source,
        @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
    final MediatorLiveData<Y> result = new MediatorLiveData<>();
    result.addSource(source, new Observer<X>() {
        LiveData<Y> mSource;

        @Override
        public void onChanged(@Nullable X x) {
            // 從Function中拿到返回的LiveData,也就是咱們新的LiveData(文章背景中同事寫的LiveData)
            LiveData<Y> newLiveData = switchMapFunction.apply(x);
            if (mSource == newLiveData) {
                return;
            }
            // remove掉舊的LiveData
            if (mSource != null) {
                result.removeSource(mSource);
            }
            mSource = newLiveData;
            if (mSource != null) {
                // add新的LiveData
                result.addSource(mSource, new Observer<Y>() {
                    @Override
                    public void onChanged(@Nullable Y y) {
                        // 通知LiveData發生變化
                        result.setValue(y);
                    }
                });
            }
        }
    });
    return result;
}
複製代碼

咱們對比一下switchMap()map()的參數類型:

  • Function<X, LiveData> switchMapFunction
  • Function<X, Y> mapFunction

很明顯一個是返回LiveData類型,一個是換一種類型。這也說明了,這倆個方法的不同之處 。

代碼解釋能夠看註釋,很直白的思路。

3.三、MediatorLiveData的使用

上述咱們看過了map()switchMap()的用法。各位應該都注意到MediatorLiveData這個類的做用。

上邊咱們一直都在操做一個LiveData,可是咱們需求很容易遇到多種狀態的變化。就像官方的demo:

LiveData liveData1 = ...;
 LiveData liveData2 = ...;

 MediatorLiveData liveDataMerger = new MediatorLiveData<>();
 liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
 liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
複製代碼

如同demo所示,咱們能夠同時add多個LiveData,根據不一樣的LiveData的變化,處理咱們不一樣的邏輯。最後經過MediatorLiveData回調到咱們的UI上。

4、源碼分析

註冊Observer

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // 若是當前生命週期是DESTROYED,直接return
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        return;
    }
    // 這個包裝類,作了一件事情,在DESTROYED,移除Observer
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // 添加在已經Observer,已存在且在Attach上後直接拋異常,也就是不能重複add
    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;
    }
    // 把Wrapper添加到LifecycleOwner上
    owner.getLifecycle().addObserver(wrapper);
}
複製代碼

Observer如何被響應:

public interface Observer<T> {
    /** * Called when the data is changed. * @param t The new data */
    void onChanged(T t);
}
複製代碼

觸發的起點,很明顯是咱們在set/postValue的時候:

@MainThread
protected void setValue(T value) {
    // 記住這個值,它是用來表示數據是否發生變化的
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    // 省略部分代碼 
    for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
        // 往裏走
        considerNotify(iterator.next().getValue());
        if (mDispatchInvalidated) {
            break;
        }
    }
    // 省略部分代碼
}

private void considerNotify(ObserverWrapper observer) {
    // 若是observer不在活動期,則直接return。也就是上述說observer不在前臺,將不會接受回調。
    if (!observer.mActive) {
        return;
    }
    // 省略部分代碼
    // 很直白的version對比
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 回調
    observer.mObserver.onChanged((T) mData);
}
複製代碼

observer.mActive在哪被賦值?不少地方。除了一些邊界條件的賦值外,一個比較正式的賦值,ObserverWrapper中的void activeStateChanged(boolean newActive)方法:

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    mActive = newActive;
}
// 此方法最終會調到此方法
@Override
boolean shouldBeActive() {
    return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
public boolean isAtLeast(@NonNull State state) {
    return compareTo(state) >= 0;
}
複製代碼

很簡單,每次生命週期回調,observer.mActive都會被賦值,而只有到Lifecycle是活動狀態是,mActive纔會true。所以只有在咱們的Activity爲前臺時咱們的LiveData纔會被回調。

尾聲

到此關於LiveData的部分就整完了,不知道各位看官是否感覺到LiveData的好用之處了呢?若是沒有,不如本身寫一寫,用身體去感覺來自LiveData的爽快~

我的公衆號:鹹魚正翻身
相關文章
相關標籤/搜索