RxJava operator (2) Transforming Observables
In the previous article, we learned how to create an Observable. Creating only one Observable may not meet complex scenarios, therefore, we may need to convert the created Observable installation rules to launch data. In this article, let's take a look at how to convert Observable
I. Buffer
As the name suggests, what the Buffer operator needs to do is to cache the specified size of Data installation, and then transmit the cached data as a set. As shown in, in the first example, we specify the buffer size as 3. After three pieces of data are collected, the data is transmitted, in the second figure, we add a skip parameter to specify how many data needs to be skipped each time a set is initiated. In the figure, how can we specify count to 2 and skip to 3, A set containing two data is sent for each three data items. If count = skip, we will find that it is equivalent to the first case.
Buffer can be cached not only by quantity rules, but also by time and other rules, such as setting a three-second cache to be released once. See the following code. We have created two Observable, and use the buffer to convert it. The first one is cached by quantity, and the second one is cached by time.
- private Observable<List<Integer>> bufferObserver() {
- return Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9).buffer(2, 3);
- }
- private Observable<List<Long>> bufferTimeObserver() {
- return Observable.interval(1, TimeUnit.SECONDS).buffer(3, TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread());
- }
Subscribe to it
- mLButton.setText("buffer");
- mLButton.setOnClickListener(e -> bufferObserver().subscribe(i -> log("buffer:" + i)));
- mRButton.setText("bufferTime");
- mRButton.setOnClickListener(e -> bufferTimeObserver().subscribe(i -> log("bufferTime:" + i)));
The running result is as follows. We can see that the first Observable emits the first two digits every three digits. The second Observable outputs two to three seconds ~ 4 digits.
Ii. FlatMap
FlatMap is a useful operator that converts data according to the rules you want before transmitting it. The principle is to convert the Observable into multiple Observable data transmitted from the original Observable as the source data, and then integrate and launch the data transmitted from the multiple Observable, note that the final sequence may be transmitted out in staggered ways. If there are strict requirements on the sequence, you can use the concatmap operator. The FlatMapIterable and FlatMap bases are the same. In contrast, Iterable is used as the source data for multiple converted Observable objects.
Next we will use FlatMap and FlatMapIterable to create and convert two Observable.
- private Observable<String> flatMapObserver() {
- return Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9).flatMap(integer -> Observable.just("flat map:" + integer));
- }
- private Observable<? extends Integer> flatMapIterableObserver() {
- return Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9)
- .flatMapIterable(
- integer -> {
- ArrayList<Integer> s = new ArrayList<>();
- for (int i = 0; i < integer; i++) {
- s.add(integer);
- }
- return s;
- }
- );
- }
Subscribe to them separately
- mLButton.setText("flatMap");
- mLButton.setOnClickListener(e -> flatMapObserver().subscribe(i -> log(i)));
- mRButton.setText("flatMapIterable");
- mRButton.setOnClickListener(e -> flatMapIterableObserver().subscribe(i -> log("flatMapIterable:" + i)));
The running result is as follows. The first operator adds the imported data with a flat map string prefix. The second operator expands the data and outputs n numbers.
Iii. GroupBy
The GroupBy operator splits the data transmitted by the original Observable into some small Observable according to the key, and then these small Observable separately transmits the data they contain, similar to the groupBy in SQL.
In use, we need to provide a key generation rule. All data with the same key will be contained in the same small Observable type. In addition, we can provide a function to convert the data, which is a bit similar to integrating flatMap.
Next, we will create two Observable objects converted by groupBy. The first one is grouped by an odd and even number, and the second one is grouped by a string prefix.
- mLButton.setText("groupBy");
- mLButton.setOnClickListener(e -> groupByObserver().subscribe(new Subscriber<GroupedObservable<Integer, Integer>>() {
- @Override
- public void onCompleted() {
- }
- @Override
- public void onError(Throwable e) {
- }
- @Override
- public void onNext(GroupedObservable<Integer, Integer> groupedObservable) {
- groupedObservable.count().subscribe(integer -> log("key" + groupedObservable.getKey() + " contains:" + integer + " numbers"));
- }
- }));
- mRButton.setText("groupByKeyValue");
- mRButton.setOnClickListener(e -> groupByKeyValueObserver().subscribe(new Subscriber<GroupedObservable<Integer, String>>() {
- @Override
- public void onCompleted() {
- }
- @Override
- public void onError(Throwable e) {
- }
- @Override
- public void onNext(GroupedObservable<Integer, String> integerIntegerGroupedObservable) {
- if (integerIntegerGroupedObservable.getKey() == 0) {
- integerIntegerGroupedObservable.subscribe(integer -> log(integer));
- }
- }
- }));
- }
The running result is as follows.
Iv. Map and Cast
The function of the Map operator is similar to FlatMap. The difference is that it directly converts data, and FlatMap needs to be implemented through some intermediate Observables.
Cast forcibly converts the data transmitted by Observable to another type, which is a specific implementation of Map.
Next we will create two Observable objects converted by map and cast.
- private Observable<Integer> mapObserver() {
- return Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9).map(integer -> integer * 10);
- }
- private Observable<Dog> castObserver() {
- return Observable.just(getAnimal())
- .cast(Dog.class);
- }
- Animal getAnimal() {
- return new Dog();
- }
- class Animal {
- protected String name = "Animal";
- Animal() {
- log("create " + name);
- }
- String getName() {
- return name;
- }
- }
- class Dog extends Animal {
- Dog() {
- name = getClass().getSimpleName();
- log("create " + name);
- }
- }
Register it
- mLButton.setText("Map");
- mLButton.setOnClickListener(e -> mapObserver().subscribe(i -> log("Map:" + i)));
- mRButton.setText("Cast");
- mRButton.setOnClickListener(e -> castObserver().subscribe(i -> log("Cast:" + i.getName())));
The result is as follows. As you can see, the map operator times the data by 10 and then transmits the data. The cast operator forcibly converts an Animal type object to a Dog type object. In addition, we can also verify the next knowledge point. When an object is created with inheritance, the constructor of the parent class will be called first.
5. Scan
The Scan operator applies a function to the data of a sequence, and transmits the result of this function as the first parameter for the next data application. It is somewhat similar to recursive operations.
Next we will create an Observable object through a list containing 10 2 objects and convert it using scan. The conversion function is to multiply the calculation result by the next number.
- private Observable<Integer> scanObserver() {
- return Observable.from(list).scan((x, y) -> x * y).observeOn(AndroidSchedulers.mainThread());
- }
Subscribe to it
- mLButton.setText("scan");
- mLButton.setOnClickListener(e -> scanObserver().subscribe(i -> log("scan:" + i)));
The result is as follows. We can see that the Npower of 2 is output.
Vi. Window
The Window operator is similar to the buffer we mentioned earlier. The difference is that window emits some small Observable objects, which are used to transmit internal data. Same as buffer, windows can be grouped not only by quantity, but also by time and other rules.
Next, we create two Observable objects that use the number of windows and time rules for grouping.
- private Observable<Observable<Integer>> windowCountObserver() {
- return Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9).window(3);
- }
- private Observable<Observable<Long>> wondowTimeObserver() {
- return Observable.interval(1000, TimeUnit.MILLISECONDS)
- .window(3000, TimeUnit.MILLISECONDS)
- .observeOn(AndroidSchedulers.mainThread());
- }
Subscribe to them separately
- mLButton.setText("window");
- mLButton.setOnClickListener(e -> windowCountObserver().subscribe(i -> {
- log(i);
- i.subscribe((j -> log("window:" + j)));
- }));
- mRButton.setText("Time");
- mRButton.setOnClickListener(e -> wondowTimeObserver().subscribe(i -> {
- log(i);
- i.observeOn(AndroidSchedulers.mainThread()).subscribe((j -> log("wondowTime:" + j)));
- }));
The running result is as follows. We can see that the first Observable object never emits a small Observable containing three data, and the second Observable object emits a 2 ~ object every 3 seconds ~ Observable object of four data items
The Transforming operator is an important embodiment of Rxjava's power. It is essential to use Rxjava flexibly to master the Transforming operator.
The demo program of this article see github: https://github.com/Chaoba/RxJavaDemo