首页
学习
活动
专区
圈层
工具
发布

RxAndroid从零开始学之四(常见操作符)

这一篇文章开始,我们要接触更复杂的一些知识点

Operator

翻译过来就是操作符的意思。什么用途呢? 先看场景分析。 一般在学校的学生花名册找一个学生的话,是通过学生的学号。然后通过学号检索出学生的详细情况。现在我们就要做这样的工作,通过学号找出学生,然后在屏幕上显示学生名字。

我们先定义一个Student.java类

代码语言:javascript
复制
public class Student {

    private long id;

    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后通过学号找学生,假设有这么一个方法getStudentInfo(long id);

代码语言:javascript
复制
public Student getStudentInfo(long id){
        Student student = new Student();
        student.setId(id);
        student.setName("test");
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return student;
    }

假设getStudentInfo()比较耗时,我们选择用RxAndroid方式编写代码。

代码语言:javascript
复制
Observable.create(new Observable.OnSubscribe<Student>() {
     @Override
     public void call(Subscriber<? super Student> subscriber) {
         subscriber.onNext(getStudentInfo(123456777));
         subscriber.onCompleted();
     }
 }).subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new Action1<Student>() {
     @Override
     public void call(Student s) {
         mTvMsg.setText(s.getName());
     }
 });

这样的代码没有什么问题。我这里只抛出一个点,因为mTvMsg这个TextView只关心的是一个String对象,上面的例子直接抛出一个Student对象与相应的Subscriber相关联。有没有更进一步呢,让Subscriber只接收String对象,其他的信息不需要关心。 答案是有的,我们可以通过map关键字。

map()

代码语言:javascript
复制
Observable.create(new Observable.OnSubscribe<Student>() {
        @Override
        public void call(Subscriber<? super Student> subscriber) {
            subscriber.onNext(getStudentInfo(123456777));
            subscriber.onCompleted();
        }
    }).map(new Func1<Student, String>() {

        @Override
        public String call(Student student) {
            return student.getName();
        }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<String>() {
        @Override
        public void call(String s) {
            mTvMsg.setText(s);
        }
    });

这段代码和上面的代码是不一样的。 也许你会觉得这很多余,但是这真的是一个极棒的检验。

试想一下,Observable对象是开发员A编写,Subscriber对象是开发员B编写,Observable现有的代码只能向外提供一个Student对象,而Subscriber只关心String类型的变量name,那好,只需要通过Student.getName()代码进行转换,要么让A重新写,要么让B重新写。但如果我非得自己写呢?不想让别人太累。这正是map的意义所在。

map()更进一步

如果用户A提供一个原始密码字符串,而用户C只关心一个加密后的字符串,我们可以在中间做一些变换。

代码语言:javascript
复制
Observable<String> pwd = Observable.just("我是密码");

Subscriber<String>  client = new Subscriber<String>() {
    ......        
   @Override
    public void onNext(String s) {
        mTvMsg.setText("密码是:"+s);
    }
};

Utils.encrypt(pwd,client);

上面的代码pwd提供原始的密码,而client只关心加密后的字符串,并显示在界面上。而Utils.encrypt()方法是我编写的一个静态的工具方法,用来进行字符加密。

Utils.java

代码语言:javascript
复制
public class Utils {

    public static void encrypt(Observable<String> pwd, Subscriber<String> result){

        pwd.map(new Func1<String, String>() {
            @Override
            public String call(String s) {
            return "我是牛逼的加密算法"+s;
            }
        }).subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(result);
    }
}

到了这里是不是感觉到爽了?

也许我还没有让你更明白,但记住,map()可以让你在最初的Observable和最终的Subscriber之间做任何转换,任何转换,任何转换。

flatmap()

很多教程在讲flatmap()这个概念的时候都显得很谨慎,怕自己讲不清楚。我在这里也尽量用很通俗直白的方式进行讲解。

我们已经见识过了map()的美妙之处,但map()有一个特点就是一对一的转换。比如

它将Observable传递过来的数据进行加工,然后再传递给Subscriber。比如String转换成String或者String转换成int类型等等。

但有一种情况是Observable传递过来的是一个数组,或者是集合。数组或者集合是没有办法直接转换成单一的类型对象的。这时候显示map()已经不太适应,而RxAndroid也提供了这种情况的解决方案。那就是flatmap().

举个例子: 假设软件开发公司A,有人数多于10名的项目经理,但是有同时进行的超过30个的项目,所以每个项目经理要同时负责不至1个软件项目,现在要打印出每个项目经理手下的开发人员的名字?怎么实现呢?先看看map的实现方式

代码语言:javascript
复制
String[] pm = new String[]{"PM1","PM2","PM3"};

Observable.from(pm)
 .map(new Func1<String, List<String>>() {
     @Override
     public List<String> call(String s) {
         //通过项目经理名字得到他负责的项目列表
         List<String> projs = getProject(s);
         return projs;
     }
 }).subscribe(new Action1<List<String>>() {
     @Override
     public void call(List<String> projs) {

         Observable.from(projs)
                 .map(new Func1<String, List<String>>() {
                     @Override
                     public List<String> call(String proj) {
                         //通过项目名称得到项目组成员的名字
                         List<String> names = getPersonNames(proj);
                         return names;
                     }
                 }).subscribe(new Action1<List<String>>() {
             @Override
             public void call(List<String> names) {
                 //打印项目组的人员的名字
                 for (String name : names) {
                     Log.d(TAG, "person name:" + name);
                 }
             }
         });


     }
 });

可以看见在Subcriber中的call中还做了一层嵌套。 而我不想让Subscrber多做一些事情,我希望在中间做一些变换,而这些变换对于Subscriber和Observable是透明的。

flatmap()可以解决这个问题。

代码语言:javascript
复制
 Observable.from(pm)
        .map(new Func1<String, List<String>>() {
            @Override
            public List<String> call(String s) {
                //通过项目经理名字得到他负责的项目列表
                List<String> projs = getProject(s);
                return projs;
            }
        }).flatMap(new Func1<List<String>, Observable<List<String>>>() {
            @Override
            public Observable<List<String>> call(List<String> projs) {
                List<List<String>> names = null;
                for(String proj:projs){
                    //通过项目名称得到项目组成员的名字
                    names.add(getPersonNames(proj));
                }
                return  Observable.from(names);
            }
        })
        .subscribe(new Action1<List<String>>() {
            @Override
            public void call(List<String> names) {

                //打印项目组的人员的名字
                for (String name : names) {
                    Log.d(TAG, "person name:" + name);
                }
            }
        });

我们在中间的部分运行了flatmap()进行了一此变换,将原来的Observable对象替换了一个新的Observable对象,然后由这个新的Observable对象来对接Subscriber,而这一切都神不知鬼不觉的,所谓移花接木。

贴一张我自己画的图

这是我自己的理解,为了防止理解不到位。贴一张官网的图片。

再总结一下,因为flatmap这个应用场景很绕。 在例子中,项目经理是一个数组,通过项目经理可以得到项目工程的列表,而项目工程列表中的各个项目可以得到项目成员的名字。

  • 原始的Observable只能提供项目经理花名册。
  • 最终的Subscriber只关心各个项目组的成员的名字。
  • 这中间的部分就是flatmap发挥作用的地方

take()

这个比较实用,直接张贴官网的说明文档

emit only the first n items emitted by an Observable

只发射指定个数的数据项。

代码语言:javascript
复制
Observable.just(1, 2, 3, 4, 5, 6, 7, 8)
          .take(4)
          .subscribe(new Subscriber<Integer>() {
        @Override
        public void onNext(Integer item) {
            System.out.println("Next: " + item);
        }

        @Override
        public void onError(Throwable error) {
            System.err.println("Error: " + error.getMessage());
        }

        @Override
        public void onCompleted() {
            System.out.println("Sequence complete.");
        }
    });

运行结果是:

Next: 1 Next: 2 Next: 3 Next: 4 Sequence complete.

filter()

这个也非常实用。可以过滤我们不需要处理的数据项,阻止它们的发射。在这里也直接贴官网文档。

emit only those items from an Observable that pass a predicate test

示例代码:

代码语言:javascript
复制
Observable.just(1, 2, 3, 4, 5)
          .filter(new Func1<Integer, Boolean>() {
              @Override
              public Boolean call(Integer item) {
                return( item < 4 );
              }
          }).subscribe(new Subscriber<Integer>() {
        @Override
        public void onNext(Integer item) {
            System.out.println("Next: " + item);
        }

        @Override
        public void onError(Throwable error) {
            System.err.println("Error: " + error.getMessage());
        }

        @Override
        public void onCompleted() {
            System.out.println("Sequence complete.");
        }
    });

运行结果如下:

代码语言:javascript
复制
Next: 1
Next: 2
Next: 3
Sequence complete.

因为在filter()中进行了处理,所以小于4的数据才正常发射被subscriber观察到。

总结

实际上RxAndroid,还有很多的Operator,甚至可以说是巨量的,但因为时间我有限,我只挑选了我觉得实用的常见的内容。大家可以去官网多去了解下。

到目前为至,我们学习的内容能够让自己将RxAndroid运用到正常的开发当中去了。

下面的内容,我会更深入。

未完待续。。。。。

下一篇
举报
领券