在上一章中,我们探索了RxJava通用过滤方法。我们学习了如何使用filter()方法过滤我们不需要的值,如何使用take()得到发射元素的子集,如何使用distinct()函数来去除重复的。我们学习了如何使用timeout()sample(),以及debounce()来利用时间。

这一章中,我们将学习如何变换可观测序列来创建一个更好满足我们需求的序列。

*map家族

RxJava提供了几个mapping函数:map(),flatMap(),concatMap(),flatMapIterable()以及switchMap().所有这些函数都作用于一个可观测序列,然后变换它发射的值,最后用一种新的形式返回它们。让我们用“真实世界”合适的例子一个个的学习下。

Map

RxJava的map函数接收一个指定的Func对象然后将它应用到每一个由Observable发射的值上。下图展示了如何将一个乘法函数应用到每个发出的值上以此创建一个新的Observable来发射转换的数据。

考虑我们已安装的应用列表。我们怎么才能够显示同样的列表,但是所有的名字都是小写。

我们的loadList()函数可以改成这样:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable.from(apps)
        .map(new Func1<AppInfo,AppInfo>(){
            @Override
            public Appinfo call(AppInfo appInfo){
                String currentName = appInfo.getName();
                String lowerCaseName = currentName.toLowerCase();
                appInfo.setName(lowerCaseName);
                return appInfo;
            }
        })
        .subscribe(new Observable<AppInfo>() {
 
            @Override
            public void onCompleted() {
                mSwipeRefreshLayout.setRefreshing(false);
            }
 
            @Override
            public void onError(Throwable e) {
                Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
                mSwipeRefreshLayout.setRefreshing(false);
            }
 
            @Override
            public void onNext(AppInfo appInfo) {
                mAddedApps.add(appInfo);
                mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
            }
        });
}
 

正如你看到的,像往常一样创建我们发射的Observable,我们加一个map调用,我们可以创建一个简单的函数来更新AppInfo对象并提供一个名字小写的新版本给观察者。

FlatMap

在复杂的场景中,我们有一个这样的Observable:它发射一个数据序列,这些数据本身也可以发射Observable。RxJava的flatMap()函数提供一种铺平序列的方式,然后合并这些Observables发射的数据,最后将合并后的结果作为最终的Observable。

当我们在处理可能有大量的Observables时,重要是记住任何一个Observables发生错误的情况,flatMap()函数将会触发它自己的onError()函数并放弃整个链。

重要的一点是关于合并部分:它允许交叉。正如上图所示,这意味着flatMap()函数在最后的Observable中不能够保证源Observables确切的发射顺序。

ConcatMap

RxJava的concatMap()函数解决了flatMap()的交叉问题,提供了一种能够把发射的值连续在一起的铺平函数,而不是合并它们,如下图所示:

FlatMapIterable

作为*map家族的一员,flatMapInterable()flatMap()很像。仅有的本质不同是它将源数据两两结成对,然后生成Iterable而不是原始数据和生成的Observables。

SwitchMap

如下图所示,switchMap()flatMap()很像,除了一点:当原始Observable发射一个新的数据(Observable)时,它将取消订阅并停止监视之前那个数据的Observable产生的Observable,并开始监视当前这一个。

Scan

RxJava的scan()函数可以看做是一个累加器函数。scan()函数对原始Observable发射的每一项数据都应用一个函数,它将函数的结果填充回可观测序列,等待和下一次发射的数据一起使用。

作为一个通用的例子,给出一个累加器:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Observable.just(1,2,3,4,5)
        .scan((sum,item) -> sum + item)
        .subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {
                Log.d("RXJAVA", "Sequence completed.");
            }
 
            @Override
            public void onError(Throwable e) {
                Log.e("RXJAVA", "Something went south!");
            }
 
            @Override
            public void onNext(Integer item) {
                Log.d("RXJAVA", "item is: " + item);
            }
        });
 

我们得到的结果是:

 
1
2
3
4
5
6
7
RXJAVA: item is: 1
RXJAVA: item is: 3
RXJAVA: item is: 6
RXJAVA: item is: 10
RXJAVA: item is: 15
RXJAVA: Sequence completed.
 

我们也可以创建一个新版本的loadList()函数用来比较每个安装应用的名字从而创建一个名字长度递增的列表。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void loadList(List<AppInfo> apps) {
    mRecyclerView.setVisibility(View.VISIBLE);
    Observable.from(apps)
            .scan((appInfo,appInfo2) -> {
                if(appInfo.getName().length > appInfo2.getName().length()){
                    return appInfo;
                } else {
                    return appInfo2;
                }
            })
            .distinct()
            .subscribe(new Observable<AppInfo>() {
 
                @Override
                public void onCompleted() {
                    mSwipeRefreshLayout.setRefreshing(false);
                }
 
                @Override
                public void onError(Throwable e) {
                    Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
                    mSwipeRefreshLayout.setRefreshing(false);
                }
 
                @Override
                public void onNext(AppInfo appInfo) {
                    mAddedApps.add(appInfo);
                    mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
                }
            });
}
 

结果如下:

有一个scan()函数的变体,它用初始值作为第一个发射的值,方法特征就像:scan(R,Func2),就像下图中的例子这样:

GroupBy

拿第一个例子开始,我们安装的应用程序列表按照字母表的顺序排序。然而,如果现在我们想按照最近更新日期来排序我们的App时该怎么办?RxJava提供了一个有用的函数从列表中按照指定的规则:groupBy()来分组元素。下图中的例子展示了groupBy()如何将发射的值根据他们的形状来进行分组。

这个函数将源Observable变换成一个发射Observables的新的Observable。它们中的每一个新的Observable都发射一组指定的数据。

为了创建一个分组了的已安装应用列表,我们在loadList()函数中引入了一个新的元素:

 
1
2
3
4
5
6
7
8
9
Observable<GroupedObservable<String,AppInfo>> groupedItems = Observable.from(apps)
    .groupBy(new Func1<AppInfo,String>(){
        @Override
        public String call(AppInfo appInfo){
            SimpleDateFormat formatter = new SimpleDateFormat("MM/yyyy");
            return formatter.format(new Date(appInfo.getLastUpdateTime()));
        }
    });
 

现在我们创建了一个新的Observable,groupedItems,将会发射一个带有GroupedObservable的序列。GroupedObservable是一个特殊的Observable,它源自一个分组的key。在这个例子中,key就是String,代表的意思是Month/Year格式化的最近更新日期。

这一点,我们已经创建了几个发射AppInfo数据的Observable,用来填充我们的列表。我们想保留字母排序和分组排序。我们将创建一个新的Observable将所有的联系起来,像通常一样然后订阅它:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Observable.concat(groupedItems)
    .subscribe(new Observable<AppInfo>() {
 
        @Override
        public void onCompleted() {
            mSwipeRefreshLayout.setRefreshing(false);
        }
 
        @Override
        public void onError(Throwable e) {
            Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
            mSwipeRefreshLayout.setRefreshing(false);
        }
 
        @Override
        public void onNext(AppInfo appInfo) {
            mAddedApps.add(appInfo);
            mAdapter.addApplication(mAddedApps.size() - 1,appInfo);
        }
    });
 

我们的loadList()函数完成了,结果是:

Buffer

RxJava中的buffer()函数将源Observable变换一个新的Observable,这个新的Observable每次发射一组列表值而不是一个一个发射。

上图中展示了buffer()如何将count作为一个参数来指定有多少数据项被包在发射的列表中。实际上,buffer()函数有几种变体。其中有一个时允许你指定一个skip值:此后每当收到skip项数据,用count项数据就填充缓存。如下图所示:

buffer()带一个timespan的参数,会创建一个每隔timespan时间段就会发射一个列表的Observable。

Window

RxJava的window()函数和buffer()很像,但是它发射的时Observable而不是列表。下图展示了window()如何缓存3个数据项并把它们作为一个新的Observable发射出去。

这些Observables中的每一个都发射原始Observable数据的一个子集,数量由count指定,最后发射一个onCompleted()结束。正如buffer()一样,window()也有一个skip变体,如下图所示:

Cast

RxJava的cast()函数是本章中最后一个操作符。它是map()操作符的特殊版本。它将源Observable中的每一项数据都转换为新的类型,把它变成了不同的Class

总结

这一章中,我们学习了RxJava时如何控制和转换可观测序列。用我们现在所学的知识,我们可以创建、过滤、转换我们所想要的任何种类的可观测序列。

下一章,我们将学习如何组合Observable,合并它们,连接它们,再或者打包它们。

RxJava开发精要5 – Observables变换的更多相关文章

  1. RxJava开发精要6 – Observables组合

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  2. RxJava开发精要4 – Observables过滤

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  3. RxJava开发精要6 - 组合Observables

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发人员头条享有独家 ...

  4. RxJava开发精要2-为什么是Observables?

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  5. RxJava开发精要7 – Schedulers-解决Android主线程问题

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  6. RxJava开发精要3-向响应式世界问好

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  7. RxJava开发精要1-从.NET到RxJava

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  8. RxJava开发精要8 – 与REST无缝结合-RxJava和Retrofit

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  9. MongoDB管理与开发精要 书摘

    摘自:<MongoDB管理与开发精要>         性能优化 创建索引 限定返回结果条数 只查询使用到的字段,而不查询所有字段 采用capped collection 采用Server ...

随机推荐

  1. Golang中解析json,构造json

    json解析是如今(网络)应用程序开发中最不可或缺的一环了.许多语言需要库支持才可以解析.构造json,但Golang凭借着原生库就可以很好地做到这一点. json的基本表现形式有两个:struct与 ...

  2. JS基本类型和引用类型的值

    JS中可以把变量分成两部分,基本类型和引用类型. 基本类型比较简单,包括:Undefined.Null.Boolean.Number和String,基本类型值就是简单的数据段:引用类型值可能由多个值构 ...

  3. 暑假集训(3)第二弹 -----Jungle Roads(Hdu1301)

    问题梗概:自从上次某个acmer来设计了拉格瑞圣岛的交通路线后,岛上的酋长就相当苦恼,他发现,虽然这些修好的公路便利了岛上的 交通,并且让拉格瑞圣岛的旅游业更加兴旺,甚至他们还收到了一笔不小的国际资金 ...

  4. 【原创】开机出现grub rescue,修复办法

    出现这种问题 一般在于进行了磁盘分区(GHOST备份时也会造成)导致grub引导文件找不到.我们只要让它找到引导文件就好了. 此时屏幕上提示grub resume>  我们先输入set看下现在g ...

  5. 支付宝api教程,支付宝根据交易号自动充值

    最近公司要用php做一个网站支付宝自动充值的功能,具体就是客户把钱直接转到公司的支付宝账号里,然后在我们网站上填写上交易号,我们网站程序自动获取交易信息,自动给网站的账户充值. 我的具体想法就是利用支 ...

  6. PH获取当前url路径及服务器路径汇总 (url 获取当前路径 服务器路径)

    以下是整理的一些, php中获取路径的小知识, 希望对你有所帮助! 1,$_SERVER["QUERY_STRING"] 说明:查询(query)的字符串 2,$_SERVER[& ...

  7. Beyond Compare 使用介绍

    Beyond Compare 背景 平时工作中对于源代码都是使用SVN来管理,在线状态下工作的很好,但是有时候离线状态下,对于多个版本之间的代码合并就比较麻烦.尤其是涉及到多人协作时更是如此. 所以找 ...

  8. CheckedListBox与下拉框联动代码

    private void yewubind(string id) { //给业务类型下拉框绑定业务类型数据 DataTable dtyewu = sb.SelectLast(id, 0); bool ...

  9. 关于修改动态库DLL的问题

    最近在做一个小软件,需要做到新建.修改动态链接库(DLL)的功能.新建很简单,但是在修改的时候我突然发现问题来了.首先软件会在启动的时候将所有协议加载出来,然后做展示.此时你可以添加或减少动态库中的属 ...

  10. 手机的touch事件(基于jquery)

    javascript代码: $.swipe=function(opt){   var o = $.extend({     mainSelector:"",     swipeLe ...