Transformations are common in RxJava, from one data type to another, or from one Observable (data source, pipe) to another.

FlatMap operator

Transform one Observables that emit data into multiple Observables, and merge their emitted data into a single Observable.

  • In daily work, flatMap is usually used to convert an Observable sending one event into an Observable sending multiple events, or convert an Observable sending multiple events into an Observable sending one event.

  • A very common example is uploading images in batches. The back end only provides a single upload interface, and images need to be compressed. Without RxJava, we would do something like this: iterate over the path collection, create the corresponding File collection, hand it to the image compression framework, set callback, batch compress, get the compressed PATH collection, iterate over the upload, set callback, aggregate the results together and return them to the external. Multiple nested for loops and callbacks can be ugly. If you have already written this code, how to write it with Rx?

public Observable<List<Images>> startUploadMultipleImage(Activity activity,
                                                             String tag,
                                                             List<String> filePaths) {
        returnObservable.just(filePaths) .flatMap(new Function<List<String>, ObservableSource<String>>() { @Override public ObservableSource<String> apply(List<String> filePaths) throws Exception { //1, compress the image, get the path of compressed file, List<String> compressFilePaths = Flora. With (activity).bitmapConfig(bitmap.config.argb_8888) .compressTaskNum(filePaths.size()) .load(filePaths) .compressSync();returnObservable.fromIterable(compressFilePaths); } }) .flatMap(new Function<String, ObservableSource<Images>>() { @Override public ObservableSource<Images> apply(String path) throws Exception { // Upload each compressed imagereturnstartUploadImage(tag, path); }}) {list.tolist ().toobservable (); }Copy the code
  • Observables. FromIterable () just creates a path that sends multiple images (only one event), then uses the flatMap operator to compress multiple images, Observable. FromIterable () iterates through the path of the compressed image file. The image is uploaded again using the flatMap (which is also called multiple times since multiple events are sent), and the final result is aggregated using the toList operator (which essentially puts the sent data into a List). The final toObservable() simply converts the result toObservable(the Single returned by toList()). There is no callback nested, no step wrapped in each operator’s callback, and no for loop nesting

  • In fact, flatMap merges data because it uses merge internally. Merge may merge data out of order. If you want to order data, concatMap is needed. So concatMap will also be introduced here.

ConcatMap operator

This is similar to flatMap, except that internal data is merged using concat, unlike flatMap, which uses merge, resulting in inconsistent order. Let’s use concatMap to modify the image upload example above

Observable. Just (filePaths) // Just (filePaths). ConcatMap (new Function<List<String>, ObservableSource<String>>() { @Override public ObservableSource<String> apply(List<String> filePaths) throws Exception { / /... Omit compression codereturn Observable.fromIterable(compressFilePaths);
                }
            })
            //...
            .toObservable();
Copy the code

The map operators

Applies a function to each piece of data emitted by an Observable, performing transformations. The simple understanding is to process and transform the data, just like an assembly line worker, to process the workpiece at the station and output it to the assembly line worker at the next station.

  • For image uploads, we might get the path of an image, but if the image compression library needs to pass in a File object, we need to convert path to file.
public Observable<Images> startUploadImage(String tag, String path) {
        FragmentActivity activity = getActivity();
        return Observable
                .just(path)
                .map(new Function<String, File>() {
                    @Override
                    public File apply(String path) throws Exception {
                        return new File(path);
                    }
                })
                .map(new Function<File, File>() {
                    @Override
                    public File apply(File file) throws Exception {
                        String compressFile = Flora.with(activity)
                                .bitmapConfig(Bitmap.Config.ARGB_8888)
                                .load(file).compressSync();
                        return new File(compressFile);
                    }
                })
                .flatMap(new Function<File, ObservableSource<HttpModel<Images>>>() {
                    @Override
                    public ObservableSource<HttpModel<Images>> apply(File file) throws Exception {
                        return UploadRequestManager.uploadImageFile(activity, tag, file);
                    }
                }).map(new Function<HttpModel<Images>, Images>() {
                    @Override
                    public Images apply(HttpModel<Images> model) throws Exception {
                        returnmodel.getData(); }}); }Copy the code

The cast operator

The operator casts every data emitted by the original Observable to a specified type and then emits data, which is a special version of the Map.

  • A cast is a cast of the event type sent upstream.

  • For example, when we update the RecyclerView item, we need to traverse the Adapter data set, find the Model class of the item we need, update the data in notifyDataSetChanged().

Object selectItemModel = onObtainCurrentSelectItemModel();
        Observable.fromIterable(mItems)
                .filter(new Predicate<Object>() {
                    @Override
                    public boolean test(Object itemModel) throws Exception {
                        return itemModel instanceof SingleSelectOptionModel;
                    }
                })
                .cast(SingleSelectOptionModel.class)
                .map(new Function<SingleSelectOptionModel, SingleSelectOptionModel>() {
                    @Override
                    public SingleSelectOptionModel apply(SingleSelectOptionModel optionModel) throws Exception {
                        if (optionModel.getItemData() == selectItemModel) {
                            optionModel.setSelect(true);
                        } else {
                            optionModel.setSelect(false);
                        }
                        return optionModel;
                    }
                })
                .all(new Predicate<SingleSelectOptionModel>() {
                    @Override
                    public boolean test(SingleSelectOptionModel optionModel) throws Exception {
                        returnoptionModel ! = null; } }) .as(RxLifecycleUtil.bindLifecycle(this)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean isOk) throws Exception {if(isOk) { mAdapter.notifyDataSetChanged(); vStatusView.showContent(); }}}, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { vStatusView.showError(); }});Copy the code
  • Observables. FromIterable () iterates through the data set and filters out the SingleSelectOptionModel. Behind the others are SingleSelectOptionModel types of data, is to use this type, you can directly use cast (SingleSelectOptionModel. Class) operator, mandatory transforms the data type of the upstream to the downstream can use directly.