关于Transformations.map和Transformations.switchMap的一些理解

用了 Android Architecture Component ** 也有一段时间了,期间也遇到过不少问题,也慢慢的解决了。mapswitchmap**是 LiveData Transformations提供的两个方法,那具体能干些啥呢?这是个好问题。如果知道RxJava,那么可以简单的对标 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;
    }

就是 传进去一个Livedata形式的参数和一个方法,然后将这个LiveData。通过方法中的逻辑再输出为LiveData,最简单的用法就是像kotlin集合中的map用法。将集合中每个元素,做你方法中的操作,比如加减之类的。源码中的那个注释大概就是这个各操作,
我们来扩展一下,假设现在有这么一个场景:界面上有个输入框,你输入一些字母 然后添加到数据库中。完了之后弹个吐司。
我们来简写一下viewmodel中的代码:

val  strLivedata=MutableLiveData<String>()
fun yourFun(str:String)="新${str}被添加到数据库中"
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 说实话,一开始的时候,我立即起来和用起来,比较费劲,尤其是配合了lamada表达式,更是难受。
先上源码:


 /**
     * 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赋值的,就是传进去userid的livedata,然后通过这个id,去获取这个用户信息。再提供一个设置userid的方法,这样流程就串起来了。我们来扩展下:输入框中输入一个字符串,然后搜索,然后调用接口或者其他,用recycleview展示出来。
先看viewModel中的代码:

//查询的关键字livedata
val query = MutableLiveData<String>()
//你点击搜索是调用的方法
   fun queryByStr(str: String) = apply { query.value = str}
//你的处理方法
fun yourFun(str:String):LiveData<List<String>>{
return 你网络或者数据库.query(str)
}

//查询结果livedata
val queryResult=LiveData<List<String>>=Transformations.switchMap(
            query,
            ::yourFun
    )

再看activity中的代码:

recycleview初始化,设置adapter就不写了

搜索.setOnClickListener{
viewModel.queryByStr(搜索框.text.toString())}
viewModel.queryResult.observe(this,Observer {
//搜索结果变化了
直接给adapter的数据源list改变,然后notify一下就完事了
})

大概基本上差不多就这样了,怎么说呢,光看代码的话,其实也不难理解,主要是本人对lamada表达是有点怵的慌,尤其是配合kotlin,本来一长串,结果给省略掉了,看起来贼费事,当然了。这个主要跟我的基础语法有关系,如果你深谙lamada表达式,那就没毛病了。总结一下。这两个操作符。map是你将你的函数用于你传参的livedata的数据通过函数体中的逻辑改变,然后将结果传到下游。而switchmap,转换跟map差不多,只不过传到下游的是livedata类型。rxjava中,map的作用是操作基本函数类型,而flatmap操作的是observable类型。当然了,还有一些能深入去理解的东西,我还没理解到。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容