前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RxJava中级进阶--map和flatmap

RxJava中级进阶--map和flatmap

作者头像
PhoenixZheng
发布2018-09-29 11:40:29
2.3K0
发布2018-09-29 11:40:29
举报

关于RxJava可以说的很多,但是要想了解RxJava的核心,只有从操作符去切入。

上一篇我们介绍了通用的Operator,像just/merge/filter。今天要介绍两个我认为是RxJava中最牛逼的Operator, map和flatmap。

在掌握map和flatmap的基础上,就可以去了解RxJava的核心操作 lift() 。可以说RxJava的核心是lift, 研究它需要对RxJava的基本操作符有一定概念和印象,很多初学者在对操作符没有印象的基础上就去了解它的核心,然后就在 lift() 这个api上被劝退了。

map()

RxJava doc对 map() 的描述如下

Returns an Observable that applies a specified function to each item emitted by the source Observable and emits the results of these function applications.

具象点地翻译过来就是, 定义一个方法用来处理异步源的每一个结果,对这些结果进行一种转换变成新的数据,然后继续下发给原来的observer。

看定义不如看代码,下面是之前通过网络获取天气情况的demo中的代码,用的是 Retrofit + RxJava,

代码语言:javascript
复制
Observable.from(cities)
          .flatMap(new Func1<String, Observable<WeatherBean>>() {
              @Override
              public Observable<WeatherBean> call(String s) {
                  ApiWeather apiWeather = retrofit.create(ApiWeather.class);
                  return apiWeather.getWeather(s, AppConstant.APP_KEY);
              }
          }).subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(new Observer<WeatherBean>() {
              ...
              @Override
              public void onNext(WeatherBean bean) {
                  String temp = String.valueOf(bean.getMain().getTemp()); //<-- 这里接收到的是WeatherBean
              }
          });

注释处的代码,接收了一个bean,而我们需要的是bean中的 temp 值,需要手动做一次转换。 我们觉得这样太不软件工程了,我想让observer直接接收到的就是一个 temp 值怎么办? 用map()! 下面是用map()修改之后的代码,注意这两段代码为了简洁都只列出了 onNext 方法。

代码语言:javascript
复制
Observable.from(cities)
        .flatMap(new Func1<String, Observable<WeatherBean>>() {
            @Override
            public Observable<WeatherBean> call(String s) {
                ApiWeather apiWeather = retrofit.create(ApiWeather.class);
                return apiWeather.getWeather(s, AppConstant.APP_KEY);
            }
        }).subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .map(new Func1<WeatherBean, String>() { //<-- 这里就是上面说的定义的方法
            @Override
            public String call(WeatherBean bean) {
                return String.valueOf(bean.getMain().getTemp()); //<-- 在这里做转换
            }
        })
        .subscribe(new Observer<String>() {
            ...
            @Override
            public void onNext(String s) {
                Log.d(TAG, "onNext: " + s); //<-- 这里就直接拿到 String 对象
            }
        });

上面就是用map改造后的样子,observer接收到的就直接是String对象。 留意一下 Func1 这个方法,它只有一个接口 call,通过泛型接收参数 T 然后返回 R, 相当于在Observer接收数据之前插入了一个转换, 放到这个例子里就是接收WeatherBean,然后转换为String返回。

所谓的map(),可以理解为一对一的变换,这也是RxJava种最基础的变换。就像在地图上去按位置查找一样。 可能有同学要喷我说,这代码不是比之前还多了几行,更不软件工程吗? 也没错,不过可以换个角度看。按之前的逻辑是接收到bean后再转换,那是命令式的思路。 使用map()之后的逻辑是在一个数据流上某个位置插入一个变换,让这个流的数据以新的方式向下派发,这是响应式/链式调用的思路。可以感受这种思维的差异。 而且从代码简洁性上来说,上面的代码在经过 lambda 简化之后是这样的

代码语言:javascript
复制
apiWeather.getWeather("Beijing", AppConstant.APP_KEY)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .map((Func1) (bean) -> {return String.valueOf(bean.getMain().getTemp());})
        .subscribe(new Observer<String>(){...});

这样的代码一眼就能看出在流程上每个步骤都做了什么事。这才是代码的魅力所在啊。

flatmap()

flatmap()是一个很牛逼的变换,如果看完这一段你还不能体会它的牛逼之处那应该是我说的不够好,希望能提建议让我知道该怎么行文才能更好的描述它。 RxJava对flatmap()的doc描述如下

Returns an Observable that emits items based on applying a function that you supply to each item emitted by the source Observable, where that function returns an Observable, and then merging those resulting Observables and emitting the results of this merger.

翻译一下

定义一个处理原始异步源返回数据的方法 func,这个方法把返回的数据再次封装,返回会派发多个结果的Observable。flatmap把这些 Observable 合并到一起,把他们的结果依次发送给Observer。

如果你的英文不错的话建议看原版英文doc,我很努力的翻译成中文还是觉得描述的不够精确。 map跟flatmap的区别在于,map 需要定义的 func 返回的不是 Observable,而只是普通的数据。而 flatmap返回的是一个 Observable。 注意这里说的是他们的参数 Func1 在返回值上的差异,不要搞成map和flatmap的返回值了,这俩的返回值都一样。

代码语言:javascript
复制
public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {...}
public final <R> Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func) {...}

如果你已经手撸过一两个RxJava的代码,或者已经对它的Observable有一定的概念,看到这里应该能明白 Func1 返回 Observable 的神奇之处。不明白也没关系,我们用代码说明。 这里用上次介绍的 just() 方法从一个数组中取出各个城市的名字,然后走网络请求获取各个城市的气温,

代码语言:javascript
复制
String[] cities = new String[]{"Beijing", "Shanghai", "Guangzhou"};
Observable.from(cities)
          .flatMap(new Func1<String, Observable<WeatherBean>>() {
              @Override
              public Observable<WeatherBean> call(String s) {
                  ApiWeather apiWeather = retrofit.create(ApiWeather.class);
                  return apiWeather.getWeather(s, AppConstant.APP_KEY);
              }
          }).subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(new Observer<WeatherBean>() {
              ...
              @Override
              public void onNext(WeatherBean bean) {
                  String temp = String.valueOf(bean.getMain().getTemp());
              }
          });

这里需要点想象力,把 cities 想象成一个异步源。这个异步源会返回多个String作为异步结果,flatmap 依据这些异步结果进一步做异步操作,再将最后的结果派发给 observer。 flatmap 表达的就是这种意思,某个异步源会派发多个数据,flatmap接受他们并进一步产生更多的数据,最后派发给observer,这是一个“铺平”的过程。回过头去看flatmap的英文定义相信你会有新的理解。

flatmap的这个特性也就带来了很牛逼的操作--异步嵌套。 比方说我们需要从服务器拿到一串城市的array,然后再去取每个城市的温度,最后输出。这种需求用普通的AsyncTask或者Handler来处理简直是灾难,但是对于RxJava来说不过是几行代码的事。 而异步嵌套甚至可以做好几层,每一层的输入是上一层的结果,你会发现在这种时候链式调用把开发效率直接提升了几个数量级。

flatmap() 和 map()

花这么多篇幅讲这两个方法是因为它们是进一步了解RxJava的切入口,看 flatmap() 的源码就明白了,

代码语言:javascript
复制
public final <R> Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func) {
    ...
    return merge(map(func));
}

public static <T> Observable<T> merge(Observable<? extends Observable<? extends T>> source) {
    ...
    return source.lift(OperatorMerge.<T>instance(false));
}

眼熟吗,flatmap是基于 map 和 merge的,而 merge 本身又用 lift 去实现。 这就是为什么我们要先熟悉 just/from/merge,map/flatmap,这些方法,再去了解 lift 的原因。

实际上在开发中掌握到 flatmap就能覆盖80%的业务需求了。而如果你想了解RxJava的高阶用法,比如自定义操作符,就不得不去了解 lift。 看到这里如果你还没被劝退,那么恭喜你已经掌握了RxJava,起码已经入门了。如果你感觉看不懂,欢迎留言不懂的地方,我尽力解释。 后面我们会分析lift和自定义操作符,希望能帮你打开新世界的大门。 源码可以后台回复"操作符"获取。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android每日一讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • map()
  • flatmap()
  • flatmap() 和 map()
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档