關(guān)于Transformations.map和Transformations.switchMap的一些理解

用了 Android Architecture Component ** 也有一段時間了,期間也遇到過不少問題,也慢慢的解決了。mapswitchmap**是 LiveData Transformations提供的兩個方法,那具體能干些啥呢?這是個好問題。如果知道RxJava,那么可以簡單的對標(biāo) map和flatmap,咱先這么理解著,對不對先不管,起碼,腦子中有個概念。

Transformations.map

先上源碼

    /**
     * Applies the given function on the main thread to each value emitted by {@code source}
     * LiveData and returns LiveData, which emits resulting values.
     * <p>
     * The given function {@code func} will be executed on the main thread.
     * <p>
     * Suppose that you have a LiveData, named {@code userLiveData}, that contains user data and you
     * need to display the user name, created by concatenating the first and the last
     * name of the user. You can define a function that handles the name creation, that will be
     * applied to every value emitted by {@code useLiveData}.
     *
     * <pre>
     * LiveData<User> userLiveData = ...;
     * LiveData<String> userName = Transformations.map(userLiveData, user -> {
     *      return user.firstName + " " + user.lastName
     * });
     * </pre>
     *
     * @param source a {@code LiveData} to listen to
     * @param func   a function to apply
     * @param <X>    a type of {@code source} LiveData
     * @param <Y>    a type of resulting LiveData.
     * @return a LiveData which emits resulting values
     */
   @MainThread
    public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
            @NonNull final Function<X, Y> func) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(func.apply(x));
            }
        });
        return result;
    }

就是 傳進(jìn)去一個Livedata形式的參數(shù)和一個方法,然后將這個LiveData。通過方法中的邏輯再輸出為LiveData,最簡單的用法就是像kotlin集合中的map用法。將集合中每個元素,做你方法中的操作,比如加減之類的。源碼中的那個注釋大概就是這個各操作,
我們來擴(kuò)展一下,假設(shè)現(xiàn)在有這么一個場景:界面上有個輸入框,你輸入一些字母 然后添加到數(shù)據(jù)庫中。完了之后彈個吐司。
我們來簡寫一下viewmodel中的代碼:

val  strLivedata=MutableLiveData<String>()
fun yourFun(str:String)="新${str}被添加到數(shù)據(jù)庫中"
fun addStrClick(str:String)= { strLivedata.value=str}
val addStr:LiveData=Transformations.map(strLivedata, ::yourFun)

然后Activity中的代碼:

viewModel.addSt.observe(this, Observer{
吐司(it)
})

button.setOnClickListener {
viewModel.addStr(edittext.text.toString())
}

恩,基本上就是這樣

Transformations.switchMap

switchmap 說實話,一開始的時候,我立即起來和用起來,比較費(fèi)勁,尤其是配合了lamada表達(dá)式,更是難受。
先上源碼:


 /**
     * Creates a LiveData, let's name it {@code swLiveData}, which follows next flow:
     * it reacts on changes of {@code trigger} LiveData, applies the given function to new value of
     * {@code trigger} LiveData and sets resulting LiveData as a "backing" LiveData
     * to {@code swLiveData}.
     * "Backing" LiveData means, that all events emitted by it will retransmitted
     * by {@code swLiveData}.
     * <p>
     * If the given function returns null, then {@code swLiveData} is not "backed" by any other
     * LiveData.
     *
     * <p>
     * The given function {@code func} will be executed on the main thread.
     *
     * <p>
     * Consider the case where you have a LiveData containing a user id. Every time there's a new
     * user id emitted, you want to trigger a request to get the user object corresponding to that
     * id, from a repository that also returns a LiveData.
     * <p>
     * The {@code userIdLiveData} is the trigger and the LiveData returned by the {@code
     * repository.getUserById} is the "backing" LiveData.
     * <p>
     * In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the
     * userIdLiveData value is set to "1", the {@code switchMap} will call {@code getUser(1)},
     * that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData
     * will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"),
     * the {@code userLiveData} gets automatically notified and will emit User(1, "Sarah").
     * <p>
     * When the {@code setUserId} method is called with userId = "2", the value of the {@code
     * userIdLiveData} changes and automatically triggers a request for getting the user with id
     * "2" from the repository. So, the {@code userLiveData} emits User(2, "John"). The LiveData
     * returned by {@code repository.getUserById(1)} is removed as a source.
     *
     * <pre>
     * MutableLiveData<String> userIdLiveData = ...;
     * LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, id ->
     *     repository.getUserById(id));
     *
     * void setUserId(String userId) {
     *      this.userIdLiveData.setValue(userId);
     * }
     * </pre>
     *
     * @param trigger a {@code LiveData} to listen to
     * @param func    a function which creates "backing" LiveData
     * @param <X>     a type of {@code source} LiveData
     * @param <Y>     a type of resulting LiveData
     */
    @MainThread
    public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
            @NonNull final Function<X, LiveData<Y>> func) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(trigger, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = func.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }

怎么理解呢,先看注釋上的那個例子。
定義一個用戶id字符串的livedata,一個用戶的livedata是通過Transformations.switchMap賦值的,就是傳進(jìn)去userid的livedata,然后通過這個id,去獲取這個用戶信息。再提供一個設(shè)置userid的方法,這樣流程就串起來了。我們來擴(kuò)展下:輸入框中輸入一個字符串,然后搜索,然后調(diào)用接口或者其他,用recycleview展示出來。
先看viewModel中的代碼:

//查詢的關(guān)鍵字livedata
val query = MutableLiveData<String>()
//你點擊搜索是調(diào)用的方法
   fun queryByStr(str: String) = apply { query.value = str}
//你的處理方法
fun yourFun(str:String):LiveData<List<String>>{
return 你網(wǎng)絡(luò)或者數(shù)據(jù)庫.query(str)
}

//查詢結(jié)果livedata
val queryResult=LiveData<List<String>>=Transformations.switchMap(
            query,
            ::yourFun
    )

再看activity中的代碼:

recycleview初始化,設(shè)置adapter就不寫了

搜索.setOnClickListener{
viewModel.queryByStr(搜索框.text.toString())}
viewModel.queryResult.observe(this,Observer {
//搜索結(jié)果變化了
直接給adapter的數(shù)據(jù)源list改變,然后notify一下就完事了
})

大概基本上差不多就這樣了,怎么說呢,光看代碼的話,其實也不難理解,主要是本人對lamada表達(dá)是有點怵的慌,尤其是配合kotlin,本來一長串,結(jié)果給省略掉了,看起來賊費(fèi)事,當(dāng)然了。這個主要跟我的基礎(chǔ)語法有關(guān)系,如果你深諳lamada表達(dá)式,那就沒毛病了??偨Y(jié)一下。這兩個操作符。map是你將你的函數(shù)用于你傳參的livedata的數(shù)據(jù)通過函數(shù)體中的邏輯改變,然后將結(jié)果傳到下游。而switchmap,轉(zhuǎn)換跟map差不多,只不過傳到下游的是livedata類型。rxjava中,map的作用是操作基本函數(shù)類型,而flatmap操作的是observable類型。當(dāng)然了,還有一些能深入去理解的東西,我還沒理解到。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容