RxJava日常使用总结(二)变换操作

在使用RxJava的过程中,变换是很常用的,从一个数据类型转换到另外一个数据类型,再或者从一个Observable(数据源、管道)转接到另外一个Observable。

flatMap操作符

将一个发射数据的Observable变换为多个Observables,然后将它们发射的数据合并后放进一个单独的Observable。

image.png
  • 日常工作中,flatMap一般用于发送一个事件的Observable,转换成发送多个事件的Observable,再或者将一个发送多个事件的Observable转换为发送一个事件的Observable。

  • 举一个很常见的例子,批量上传图片。后端只提供单个上传的接口,图片需要压缩。如果不用RxJava的话,我们一般会这么做:遍历图片path集合,创建对应的File集合,再交给图片压缩框架,设置callback,批量压缩,获取压缩后的图片path集合,再遍历上传,设置callback,多个结果聚合在一起,返回给外部。多个for循环和callback嵌套就会很难看,相信大家都写过这样的代码,那么用Rx要怎么写呢?

public Observable<List<Images>> startUploadMultipleImage(Activity activity,
                                                             String tag,
                                                             List<String> filePaths) {
        return Observable.just(filePaths)
                .flatMap(new Function<List<String>, ObservableSource<String>>() {
                    @Override
                    public ObservableSource<String> apply(List<String> filePaths) throws Exception {
                        //1、压缩图片,得到压缩文件的path,遍历
                        List<String> compressFilePaths = Flora
                                .with(activity)
                                .bitmapConfig(Bitmap.Config.ARGB_8888)
                                .compressTaskNum(filePaths.size())
                                .load(filePaths)
                                .compressSync();
                        return Observable.fromIterable(compressFilePaths);
                    }
                })
                .flatMap(new Function<String, ObservableSource<Images>>() {
                    @Override
                    public ObservableSource<Images> apply(String path) throws Exception {
                        //2、对每张压缩图片进行上传
                        return startUploadImage(tag, path);
                    }
                })
                //3、结果堆积为List
                .toList()
                .toObservable();
    }
  • 可以看到,先just创建一个发送一个多个图片的path集合(只发送一次事件),再使用flatMap操作符,压缩多张图片后,Observable.fromIterable(),遍历发送压缩后的图片文件的path(发送多次事件),再使用一次flatMap进行图片上传操作(因为是多个事件发送,这里的flatMap也是被调用多次),最后的结果使用toList操作符聚合在一起(其实就是将发送的数据放到一个List再发出),最后的toObservable()只是将结果转换类型为Observable(toList()返回的Single)。一条龙下来,没有任何的callback嵌套,没一步都包裹在每个操作符的回调中,for循环嵌套也不会出现了~
  • 其实flatMap对数据的合并,因为内部是使用merge,merge是合并数据时是可能有无序的,如果想有序,就需要使用concatMap。所以这里将concatMap也介绍一下。

concatMap操作符

和flatMap类似,不过内部合并数据是使用concat,而不像flatMap是使用merge,导致顺序可能会不一致。我们使用concatMap改造上面图片上传的例子吧~

Observable.just(filePaths)
              //其实只改了这里
            .concatMap(new Function<List<String>, ObservableSource<String>>() {
                @Override
                public ObservableSource<String> apply(List<String> filePaths) throws Exception {
                        //...省略压缩代码
                    return Observable.fromIterable(compressFilePaths);
                }
            })
            //...
            .toObservable();

map操作符

对Observable发射的每一项数据应用一个函数,执行变换操作。简单理解就是将数据做处理转换,就像流水线工人,对流到工位的工件做处理,再输出给下个工位的流水线工人。

image.png
  • 还是图片上传的例子,针对单图上传,我们可能会得到一个图片的path,然而如果图片压缩库需要传入的是file对象,这时候就需要将path转换为file对象。
public Observable<Images> startUploadImage(String tag, String path) {
        FragmentActivity activity = getActivity();
        return Observable
                .just(path)
                .map(new Function<String, File>() {
                    @Override
                    public File apply(String path) throws Exception {
                        return new File(path);
                    }
                })
                .map(new Function<File, File>() {
                    @Override
                    public File apply(File file) throws Exception {
                        String compressFile = Flora.with(activity)
                                .bitmapConfig(Bitmap.Config.ARGB_8888)
                                .load(file).compressSync();
                        return new File(compressFile);
                    }
                })
                .flatMap(new Function<File, ObservableSource<HttpModel<Images>>>() {
                    @Override
                    public ObservableSource<HttpModel<Images>> apply(File file) throws Exception {
                        return UploadRequestManager.uploadImageFile(activity, tag, file);
                    }
                }).map(new Function<HttpModel<Images>, Images>() {
                    @Override
                    public Images apply(HttpModel<Images> model) throws Exception {
                        return model.getData();
                    }
                });
    }

cast操作符

操作符将原始Observable发射的每一项数据都强制转换为一个指定的类型,然后再发射 数据,它是 map 的一个特殊版本。

image.png
  • 简单理解,cast其实就是对上游发送过来的事件类型做一个强制类型转换。

  • 举一个例子,我们对RecyclerView做条目更新时,需要遍历Adapter的数据集,找到我们需要的条目的Model类,更新里面的数据,在notifyDataSetChanged()。

Object selectItemModel = onObtainCurrentSelectItemModel();
        Observable.fromIterable(mItems)
                .filter(new Predicate<Object>() {
                    @Override
                    public boolean test(Object itemModel) throws Exception {
                        return itemModel instanceof SingleSelectOptionModel;
                    }
                })
                .cast(SingleSelectOptionModel.class)
                .map(new Function<SingleSelectOptionModel, SingleSelectOptionModel>() {
                    @Override
                    public SingleSelectOptionModel apply(SingleSelectOptionModel optionModel) throws Exception {
                        if (optionModel.getItemData() == selectItemModel) {
                            optionModel.setSelect(true);
                        } else {
                            optionModel.setSelect(false);
                        }
                        return optionModel;
                    }
                })
                .all(new Predicate<SingleSelectOptionModel>() {
                    @Override
                    public boolean test(SingleSelectOptionModel optionModel) throws Exception {
                        return optionModel != null;
                    }
                })
                .as(RxLifecycleUtil.bindLifecycle(this))
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean isOk) throws Exception {
                        if (isOk) {
                            mAdapter.notifyDataSetChanged();
                            vStatusView.showContent();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        vStatusView.showError();
                    }
                });
  • 在上面,mItems是一个Object类型的集合,Observable.fromIterable()遍历数据集,再filter过滤操作符,将SingleSelectOptionModel类型的数据都过滤掉了,剩下的都是SingleSelectOptionModel类型的数据,后面都要使用这个类型时,就可以直接使用cast(SingleSelectOptionModel.class)操作符,强制将上游的数据做类型转换~下游就可以直接使用了。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354