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原理圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章