RxJava的车间操作工人-操作符

如果你对RxJava1.x还不是了解,可以参考下面文章。

1. RxJava使用介绍 【视频教程】
2. RxJava操作符
  • Creating Observables(Observable的创建操作符) 【视频教程】
  • Transforming Observables(Observable的转换操作符) 【视频教程】
  • Filtering Observables(Observable的过滤操作符) 【视频教程】
  • Combining Observables(Observable的组合操作符) 【视频教程】
  • Error Handling Operators(Observable的错误处理操作符) 【视频教程】
  • Observable Utility Operators(Observable的辅助性操作符) 【视频教程】
  • Conditional and Boolean Operators(Observable的条件和布尔操作符) 【视频教程】
  • Mathematical and Aggregate Operators(Observable数学运算及聚合操作符) 【视频教程】
  • 其他如observable.toList()、observable.connect()、observable.publish()等等; 【视频教程】
3. RxJava Observer与Subcriber的关系 【视频教程】
4. RxJava线程控制(Scheduler) 【视频教程】
5. RxJava 并发之数据流发射太快如何办(背压(Backpressure)) 【视频教程】


在RxJava中,如果把整个事件流看作是工厂的流水线,Observable就是原料,Observer就是我们的产品经理,这个产品是怎么交到我们产品经理手上的呢? 中间很重要的就是工人,也就是操作符。它负责在Observable发出的事件和Observable的响应之间做一些处理。

首先我们来看一段Java代码:

static List<Student> studentList = new ArrayList<Student>(){
        {
            add(new Student("Stay", 28));
            add(new Student("谷歌小弟", 23));
            add(new Student("Star", 25));
        }
    };
List<Student> list = new ArrayList<Student>();
        new Thread(new Runnable(){
            @Override public void run(){
                synchronized (studentList) {
                    for(Student student : studentList) {
                        if(student.age > 23){
                            list.add(student);
                        }
                    }
                }
                runOnUiThread(new Runnable() {
                      @Override
                       public void run() {
                           //UI显示过滤后的数据
                           displayUI(list);
                       }
                   });
            }
        }).start();

此段代码实现的是过滤数据,也就是统计年龄大于23的学生,并将过滤数据显示在UI上。

使用RxJava来实现(体验一下RxJava的链式结构):

Observable.from(studentList)    //from可以理解为创建一个循环
        .filter(new Func1<Student, Boolean>() {//filter代表根据规则过滤
            @Override
            public Boolean call(Student student) {
                return student.age > 23; //过滤年龄大于23的学生
            }
        }).subscribeOn(Schedulers.newThread()) //任务在新线程里运行
        .toList()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<List<Student>>() {
            @Override
            public void call(List<Student> list) {
                //UI显示过滤后的数据
                displayUI(list);
            }
        });

可以看到,代码减少了,而且逻辑也很清晰。

下面我们再看个复杂点的场景:找出SD卡所有的.png格式的文件。

String basePath = Environment.getExternalStorageDirectory().getPath();
        File rootFile = new File(basePath);

        Observable.just(rootFile)
                .flatMap(new Func1<File, Observable<File>>() {
                    @Override
                    public Observable<File> call(File file) {//遍历文件夹
                        return listFiles(file);
                    }
                })
                .filter(new Func1<File, Boolean>() {//过滤图片,二级文件夹中的图片无法找出
                    @Override
                    public Boolean call(File file) {
                        return file.getName().endsWith(".png");
                    }
                })
                .map(new Func1<File, String>() {
                    @Override
                    public String call(File file) {
                        return file.getPath();
                    }
                })
                .toList()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<String>>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onNext(List<String> list) {//返回png格式图片列表
                        //do something
                    }
                });

其中,listFiles方法如下:

/**
     * 递归查询目录中的文件
     * @param f
     * @return
     */
    public static Observable<File> listFiles(final File f){
        if(f.isDirectory()){
            return Observable.from(f.listFiles()).flatMap(new Func1<File, Observable<File>>() {
                @Override
                public Observable<File> call(File file) {
                    /**如果是文件夹就递归**/
                    return listFiles(file);
                }
            });
        } else {
            /**filter操作符过滤,是文件就通知观察者**/
            return Observable.just(f).filter(new Func1<File, Boolean>() {
                @Override
                public Boolean call(File file) {
                    return f.exists() && f.canRead() ;
                }
            });
        }
    }

从上面这段代码我们可以看到:虽然代码量看起来变复杂了,但是RxJava的实现是一条链式调用,没有任何的嵌套;整个实现逻辑看起来非常简洁清晰,这对我们的编程实现和后期维护是有巨大帮助的。特别是对于那些回调嵌套的场景。

有没有心动,喜欢上RxJava了呢?

将非Rx方法转换成Rx方法

/**
 * 模拟从数据库获取课程列表
 * @return
 */
private static  List<String> getCoursesFromDatabase() {
    List<String> courseList = new ArrayList<>();
    courseList.add("美术");
    courseList.add("体育");
    courseList.add("音乐");
    return courseList;
}

在RxJava中,just和from操作符可以把一个对象转换成Observable,上面的方法使用just改写:

private static Observable<List<String>> getAllCourses() {
     return Observable.just(getCoursesFromDatabase());
}

不过这样改写会有个问题,只要调用getAllCourses方法就会运行getCoursesFromDatabase方法,那么就不能通过指定Schedules的方式实现异步查询了。

所以如果某个方法经常会被异步调用,我们往往会使用create来改写:

private static Observable<List<String>> getAllCourses() {
        return Observable.create(new OnSubscribe<List<String>>() {

            @Override
            public void call(Subscriber<? super List<String>> subscriber) {
                 List<String> courseList = getCoursesFromDatabase();
                subscriber.onNext(courseList);
                subscriber.onCompleted();
            }
        });
    }

在使用Create操作符时,getCoursesFromDatabase方法是写在OnSubscribe接口的call方法里。调用getAllCourses方法,getCoursesFromDatabase并不会马上运行。

getAllCourses(db)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Subscriber<String>(){...});

注意:
因为just(),from()这类创建Observable的操作符在创建之初,就已经存储了对象的值,而不是在被订阅的时候才创建。所以在我们订阅之前,getCoursesFromDatabase()方法就已经在开始执行了,这样就不能达到我们想要的效果。

丰富的操作符

RxJava中的操作符可以让你对数据流做任何操作。

将一系列的操作符链接起来就可以完成复杂的逻辑。代码被分解成一系列可以组合的片段。这就是响应式函数编程的魅力。用的越多,就会越多的改变你的编程思维。

另外,RxJava也使我们处理数据的方式变得更简单。在最后一个例子里,我们调用了两个API,对API返回的数据进行了处理,然后保存到磁盘。 但是我们的Subscriber并不知道这些,它只是认为自己在接收一个Observable<String>对象。良好的封装性也带来了编 码的便利!

RxJava的强大性就来自于它所定义的操作符。

RxJava包含了大量的操作符。操作符的数量是有点吓人,但是很值得你去挨个看一下,这样你可以知道有哪些操作符可以使用。弄懂这些操作符可能会花一些时间,但是一旦弄懂了,你就完全掌握了RxJava的威力。

你甚至可以编写自定义的操作符!这篇blog不打算讨论自定义操作符,如果你想的话,请自行Google吧。

目前为止,我们已经接触了filter、just、create三个操作符,RxJava中还有更多的操作符,那么我们如何使用其他的操作符来改进我们的代码呢?不要着急,后面会分类学习RxJava的操作符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值