Android框架——RxJava(三)转换操作符

RxJava(三)转换操作符

RxJava学习系列

上一篇博客已经讲解了RxJava的API的基本使用,这里我们来学习下RxJava的另一个特性——对数据流的转换操作符。

RxJava中提供的操作符可以分为转换类操作符,过滤类操作符以及组合类操作符,接下来我将依次对这三种操作符的使用进行讲解。


一、转换类操作符

转换类操作符顾名思义,就是将上游(被观察者)发送的数据流进行转换成另外一种形式(观察者希望接收到的形式),从而使下游(观察者)能够正确的接受并作出响应。

这里介绍mapcastflatMapconcatMapflatMapIterableswitchMapscanbufferwindowgroupBy进行讲解

map

map的方法声明为:Observable<R> map(Func1<? super T, ? extends R> func),其中Func1<T, R>接口里面只包含一个R call(T t)方法,从参数和返回值就可以看出来这是一个将T类型转换成一个R类型的方法。FuncX类的接口表示有X+1个泛型,前X个是call方法的参数,最后一个是call方法的返回值。

所以map的作用就是将接收类型为T的Observable转换为接收类型为R的Observable

RxJava官方给出的Map原理图:

Map原理图

Map的使用也很简单,比如说现在有一个Student列表,上游发出的数据流是Student格式,但是下游希望接收到的格式是Student对象的成绩(Integer格式),代码如下:

Observable.from(studentList)
        .map(new Func1<Student, Integer>() {
            @Override
            public Integer call(Student student) {
              return student.getGrade();
            }
        })
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {
              System.out.println("成绩为:" + integer);
        }
    });

上述代码可以理解为from传入数据流后,经过map函数,转换成Integer,再到达subscriber,调用事件回调函数。

cast

cast运算符作用和map类似,但是它只能将父类对象转换为子类对象,是map的特殊版本。
方法声明:Observable<R> cast(final Class<R> klass),将源事件类型转换成klass的数据类型,注意只能将父类对象转为子类对象
PersonStudent的父类:

List<Person> personList = new ArrayList<>(studentList);
Observable.from(personList)
    .cast(Student.class)
    .subscribe(student -> System.out.println(student.getId()));

cast执行转化必须是可以转的,如果该父类对象无法转为子类对象,会抛出ClassCastException异常

原理图如下:

cast原理图

flatMap

现在比如给出一个年级的所有班级的列表ArrayList<IClass>,每个IClass对象都包含内部学生的列表ArrayList<Student>,我要输出每个班人的成绩。我当然可以这样做:

Observable.from(iClassList)
    .subscribe(new Action1<IClass>() {
      @Override
      public void call(IClass iClass) {
        for (Student student : iClass.getStudentList()) {
          System.out.println(student.getGrade());
        }
      }
    });

那么如果我不想在事件回调的时候去遍历,我就想让事件回调时接受到的就是每个Student,而不是一个班级IClass,该如何做呢?

这个时候就需要用到flatMap这个操作符了,先看看具体的实现:

Observable.from(iClassList)
    .flatMap(new Func1<IClass, Observable<Student>>() {
      @Override
      public Observable<Student> call(IClass iClass) {
        return Observable.from(iClass.getStudentList());
      }
    })
    .subscribe(new Action1<Student>() {
      @Override
      public void call(Student student) {
        System.out.println(student.getGrade());
      }
    });

flatMap的参数形式为Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func),传入一个T类型,返回一个接收事件为R类型的Observable,在结合上面的例子,就很好理解flatMap的作用了。
上述代码调用流程:

  • flatMap依次将传入的每一个iClass对象转换为一个Observable<Student>对象,在这个Observable中传入iClass.getStudentList()事件列表
  • 最后将所有Observable<Student>对象中的事件都汇集到一个Observable之中,这个Obsevable就是flatMap的返回值,由这个Observable将所有的事件发给SubscriberSubscriber再对每个事件依次调用事件回到函数。

flatMap的缺点在于它并不一定会按照原始Observable的顺序发送事件,可能会有事件的交叉发送的情况。

原理图如下:

flatMap原理图

concatMap

上面也讲到了flatMap的缺点,那么如何解决交叉的问题呢?RxJava给我们提供了concatMap这个方法,它用来将最初发送的事件按照原来的顺序连续发送出去。使用方法跟flatMap一样。

Observable<R> concatMap(Func1<? super T, ? extends Observable<? extends R>> func)

原理图如下:

concatMap原理图

flatMapIterable

flatMapIterableflatMap相差无几,区别在于flatMapIterable转换后的是一个Iterable对象,最后将所有的Iterable都传入一个Observable中,由这个Observable来将事件发送出去。

原理图如下:

flatMapIterable原理图

示例代码如下:

Observable.from(iClassList)
    .flatMapIterable(new Func1<IClass, Iterable<Student>>() {
      @Override
      public Iterable<Student> call(IClass iClass) {
        return iClass.getStudentList();
      }
    })
    .subscribe(new Action1<Student>() {
      @Override
      public void call(Student student) {
        System.out.println(student.getGrade());
      }
    });

switchMap

switchMap的作用也和flatMap相似,但是它在连续发送事件的时候如果前一个事件未订阅完成,后一个事件就已经到达,此时会将前一个事件直接取消,可多用于网络请求之中。

Observable.just("first", "second", "third", "fourth", "fifth")
    .switchMap(new Func1<String, Observable<String>>() {
      @Override
      public Observable<String> call(String s) {
        return Observable.just(s).subscribeOn(Schedulers.newThread());
      }
    })
    .subscribe(new Subscriber<String>() {
      @Override
      public void onCompleted() {
        System.out.println("success");
      }

      @Override
      public void onError(Throwable e) {

      }

      @Override
      public void onNext(String s) {
        System.out.println(s);
      }
    });

上述代码每次产生事件都是新开一个线程,这样有可能前面的事件在订阅未完成时,后面的事件已经到来,这样就会取消前一个事件,这种情况只有在并发的情况下才会发生,同一个线程事件先后发送执行是不会出现这种情况的。至于最后出现的结果,由于线程的调用取决于CPU的调度,所以会出现不同的情况,详情可以参看RxJava的源码分析。

原理图如下:

switchMap原理图

scan

scan就是将前一个事件产生的结果发送到下一个事件转换时的第一个参数使用。
Observable<T> scan(Func2<T, T, T> accumulator)
Observable<R> scan(R initialValue, Func2<R, ? super T, R> accumulator)
Func2接口的三个泛型,前两个是内部call方法的调用参数,第三个是call的返回值
先说第一个重载,第一个T是前一个事件经过Func2.call方法后的返回值,第二个T是当前事件,第三个T是调用call方法后的返回值,作为下一个事件的Func2的第一个参数,至于第一个事件就是默认无初始计算结果了。第二个重载就是给第一个事件设置一个初始的计算结果。

代码如下:

Observable.just(1, 2, 3, 4, 5)
    .scan(new Func2<Integer, Integer, Integer>() {
      @Override
      public Integer call(Integer sum, Integer item) {
        return sum + item;
      }
    }).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: 3
Next: 6
Next: 10
Next: 15
Sequence complete.

原理图:

scan原理图

buffer

buffer就是将原有的事件转换成一个List列表,由一个新的Observable一次性发送出去,方法声明如下:

Observable<List<T>> buffer(int count)
Observable<List<T>> buffer(int count, int skip)

第一种重载内部就是调用了buffer(count, count),所以就直接分析第二个重载。countskip的含义为:把每skip个事件中的前count个事件转换成一个List列表,剩余的丢弃,再由新的Observable将这个列表发送出去。比如现在有一个StudentList,id分别是从U1001到U1008,总共八个数据,如果调用如下代码:

Observable.from(studentList)
    .buffer(3, 4)
    .subscribe(studentList1 -> {
      for (Student student : studentList1) {
        System.out.println(student.getId());
      }
    });

结果为:

U1001
U1002
U1003
U1005
U1006
U1007

可见U1004和U1008被丢弃了,如果调用buffer(4,5),结果如下:

U1001
U1002
U1003
U1004
U1006
U1007
U1008

U1005是第一组5个中的第5个被丢弃了,U1006-U1008是第二组5个中的前三个,由于第4个不存在,所以发送第三个后就结束发送了。

原理图如下:

buffer原理图

window

windowbuffer的作用非常相似,区别在于buffer发送出去的是一个列表List,而window发送出去的事件是一个Observable对象,方法声明如下:

Observable<Observable<T>> window(int count)
Observable<Observable<T>> window(int count, int skip)

所以在windowsubscribe中得到的是一个Observable对象,需要再次调用subscribe方法,去获取其中的数据,代码如下:

Observable.from(studentList)
    .window(3, 5)
    .subscribe(observable -> observable.subscribe(student -> System.out
        .println(student.getId())));

以上是lambda表达式的写法,不清楚的可以去学习一下Lambda表达式。

原理图如下:

window原理图

groupBy

groupBy的常用调用方式如下:
Observable<GroupedObservable<K, T>> groupBy(final Func1<? super T, ? extends K> keySelector)

groupBy通过keySelector将发送的事件分为不同的组,并将每一组以Observable<GroupObserverable>的形式发送出去。keySelector第一个参数为call方法传入的事件类型,第二个参数是返回的key

比如发出的事件源为学生列表,但是我想将其分为及格的和不及格的分别处理,代码如下

List<Student> studentList = new ArrayList<>();
studentList.add(new Student("U1111", 59));
studentList.add(new Student("U1112", 69));
studentList.add(new Student("U1113", 37));
studentList.add(new Student("U1114", 72));
studentList.add(new Student("U1115", 26));
studentList.add(new Student("U1116", 98));
studentList.add(new Student("U1117", 53));
studentList.add(new Student("U1118", 60));
studentList.add(new Student("U1119", 100));
Observable.from(studentList)
    .groupBy(new Func1<Student, Integer>() {
      @Override
      public Integer call(Student student) {
        return student.getGrade() >= 60 ? 1 : 0;
      }
    })
    .subscribe(new Action1<GroupedObservable<Integer, Student>>() {
      @Override
      public void call(GroupedObservable<Integer, Student> integerStudentGroupedObservable) {
        integerStudentGroupedObservable.subscribe(new Action1<Student>() {
          @Override
          public void call(Student student) {
            if (integerStudentGroupedObservable.getKey() == 0) {
              System.out.println("不及格的ID为" + student.getId());
            } else {
              System.out.println("及格的ID为" + student.getId());
            }
          }
        });
      }
    });

运行代码结果为:

不及格的ID为U1111
及格的ID为U1112
不及格的ID为U1113
及格的ID为U1114
不及格的ID为U1115
及格的ID为U1116
不及格的ID为U1117
及格的ID为U1118
及格的ID为U1119

原理图如下:

groupBy原理图

猜你喜欢

转载自blog.csdn.net/boyeleven/article/details/81867256
今日推荐