RXJava conversion of (III)

Ali P7 mobile Internet architect, Advanced Video (Daily update) for free learning, please click: https://space.bilibili.com/474380680

Convert

RxJava provides support for transforming the sequence of events, which is one of its core functions, but also most people say, "RxJava really handy," the biggest reason. The so-called conversion, the sequence of events is an object or the entire sequence of processing, converted to a different event or sequence of events. He said always difficult to understand the concept of fuzzy, look API.

1) API

First, a look at map()an example:

Observable.just("images/logo.png") // 输入类型 String
    .map(new Func1<String, Bitmap>() {
        @Override
        public Bitmap call(String filePath) { // 参数类型 String
            return getBitmapFromPath(filePath); // 返回类型 Bitmap
        }
    })
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) { // 参数类型 Bitmap
            showBitmap(bitmap);
        }
    });

Here there is a named Func1class. It Action1is very similar, is RxJava an interface, a method for packaging containing a parameter. Func1And the Actiondifference is that the Func1packaging is a method that returns a value. Further, and ActionXas FuncXthere are a plurality of process parameters for the different number. FuncXAnd ActionXdifferences in FuncXthe packaging method of the return value is.

We can see, map()method parameter Stringobject into a Bitmapsubject upon return, while after map()the method, also by the type of event parameters Stringinto the Bitmap. This direct conversion object and returns, is the most common and most easily understood transformations. But RxJava transform much more than this, it is not only possible, but also for the entire event queue for the event object, which makes RxJava very flexible. I have listed a few common transformation:

  • map(): Direct conversion event object, the specific function above has been introduced. It is most commonly used RxJava transformation. map()Schematic:

     
    19956127-712286962b882f13.jpg
    map () schematic

     

  • flatMap(): This is a very useful, but very difficult to understand the transformation, so I decided to spend more space to introduce it. First, assuming that such a need: Suppose there is a data structure "Student", now need to print out the name of a group of students. Implementation is very simple:

Student[] students = ...;
Subscriber<String> subscriber = new Subscriber<String>() {
    @Override
    public void onNext(String name) {
        Log.d(tag, name);
    }
    ...
};
Observable.from(students)
    .map(new Func1<Student, String>() {
        @Override
        public String call(Student student) {
            return student.getName();
        }
    })
    .subscribe(subscriber);

Very simple. Then another hypothesis: the name of each student in all courses required for repair if you want to print it out? (The difference between the demand that each student has only one name, but there is more than one course.) First of all can be achieved:

Student[] students = ...;
Subscriber<Student> subscriber = new Subscriber<Student>() {
    @Override
    public void onNext(Student student) {
        List<Course> courses = student.getCourses();
        for (int i = 0; i < courses.size(); i++) {
            Course course = courses.get(i);
            Log.d(tag, course.getName());
        }
    }
    ...
};
Observable.from(students)
    .subscribe(subscriber);

It is still very simple. So if I do not want to Subscriberuse a for loop, but to Subscriberdirect incoming individual Courseobjects it (which is important for code reuse)? With map()apparently not enough, because map()one to one conversion, but I now request is transforming many. Then how can the Student converted into a more Course it?

This time, we need to use flatMap()the:

Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
    @Override
    public void onNext(Course course) {
        Log.d(tag, course.getName());
    }
    ...
};
Observable.from(students)
    .flatMap(new Func1<Student, Observable<Course>>() {
        @Override
        public Observable<Course> call(Student student) {
            return Observable.from(student.getCourses());
        }
    })
    .subscribe(subscriber);

As can be seen from the above code, flatMap()and map()return it is another object of the passed parameters after conversion: a same point. Note, however, and map()the difference is that flatMap()in return is Observablethe object, and the Observableobject is not being sent directly to the Subscribercallback method. flatMap()The principle is this: 1. Create a passed in the event object Observable. Object; 2 does not send this Observable. But to activate it, then it starts sending events; create a 3 out of every Observableevent that is sent, were import the same Observable, and this Observableis responsible for these events will be unified to the Subscribercallback method. These three steps, the event split into two, through a newly created group Observabledistributed down after the initial object "pave" a unified path. And this "pave" is the flatMap()so-called flat.

flatMap() schematic diagram:

 
19956127-4313ae6988dbe124.jpg
flatMap () schematic

Extensions: since nested Observableadded asynchronous code, flatMap()also commonly used for nested asynchronous operation, such as nested network requests. Sample Code (Retrofit + RxJava):

networkClient.token() // 返回 Observable<String>,在订阅时请求 token,并在响应后发送 token
    .flatMap(new Func1<String, Observable<Messages>>() {
        @Override
        public Observable<Messages> call(String token) {
            // 返回 Observable<Messages>,在订阅时请求消息列表,并在响应后发送请求到的消息列表
            return networkClient.messages();
        }
    })
    .subscribe(new Action1<Messages>() {
        @Override
        public void call(Messages messages) {
            // 处理显示消息列表
            showMessages(messages);
        }
    });

Traditional nested Callback request requires use nested achieved. Through flatMap(), the write request can be nested in one strand, thereby maintaining clarity of program logic.

  • throttleFirst(): Discard new events within a certain time interval after each trigger event. It is often used as a de-jitter filter, such as a button click listener: RxView.clickEvents(button) // RxBinding 代码,后面的文章有解释 .throttleFirst(500, TimeUnit.MILLISECONDS) // 设置防抖间隔为 500ms .subscribe(subscriber);my mother no longer afraid of my tremor user interface points to open two repeat it.

In addition, RxJava also provides many convenient ways to achieve transformation sequence of events, there is not one by one example.

2) the principle of transformation: lift ()

这些变换虽然功能各有不同,但实质上都是针对事件序列的处理和再发送。而在 RxJava 的内部,它们是基于同一个基础的变换方法: lift(Operator)。首先看一下 lift() 的内部实现(仅核心代码):

// 注意:这不是 lift() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。
public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);
        }
    });
}

这段代码很有意思:它生成了一个新的 Observable 并返回,而且创建新 Observable 所用的参数 OnSubscribe 的回调方法 call() 中的实现竟然看起来和前面讲过的 Observable.subscribe() 一样!然而它们并不一样哟~不一样的地方关键就在于第二行 onSubscribe.call(subscriber) 中的 onSubscribe 所指代的对象不同(高能预警:接下来的几句话可能会导致身体的严重不适)——

  • subscribe() 中这句话的 onSubscribe 指的是 Observable 中的 onSubscribe 对象,这个没有问题,但是 lift() 之后的情况就复杂了点。
  • 当含有 lift() 时:
    1.lift() 创建了一个 Observable 后,加上之前的原始 Observable,已经有两个 Observable 了;
    2.而同样地,新 Observable 里的新 OnSubscribe 加上之前的原始 Observable 中的原始 OnSubscribe,也就有了两个 OnSubscribe
    3.当用户调用经过 lift() 后的 Observablesubscribe() 的时候,使用的是 lift() 所返回的新的 Observable ,于是它所触发的 onSubscribe.call(subscriber),也是用的新 Observable 中的新 OnSubscribe,即在 lift() 中生成的那个 OnSubscribe
    4.而这个新 OnSubscribecall() 方法中的 onSubscribe ,就是指的原始 Observable 中的原始 OnSubscribe ,在这个 call()方法里,新 OnSubscribe 利用 operator.call(subscriber) 生成了一个新的 SubscriberOperator 就是在这里,通过自己的 call() 方法将新 Subscriber 和原始 Subscriber 进行关联,并插入自己的『变换』代码以实现变换),然后利用这个新 Subscriber 向原始 Observable 进行订阅。
    这样就实现了 lift() 过程,有点像一种代理机制,通过事件拦截和处理实现事件序列的变换。

精简掉细节的话,也可以这么说:在 Observable 执行了 lift(Operator) 方法之后,会返回一个新的 Observable,这个新的 Observable 会像一个代理一样,负责接收原始的 Observable 发出的事件,并在处理后发送给 Subscriber

如果你更喜欢具象思维,可以看图:

 
19956127-4fc0d8e83d07dfda.jpg
lift() 原理图

或者可以看动图:

 
19956127-8f0a34dc10d694f4.gif
lift 原理动图

两次和多次的 lift() 同理,如下图:

 
19956127-eaef8cbde2224548.jpg
两次 lift

举一个具体的 Operator 的实现。下面这是一个将事件中的 Integer 对象转换成 String 的例子,仅供参考:

observable.lift(new Observable.Operator<String, Integer>() {
    @Override
    public Subscriber<? super Integer> call(final Subscriber<? super String> subscriber) {
        // 将事件序列中的 Integer 对象转换为 String 对象
        return new Subscriber<Integer>() {
            @Override
            public void onNext(Integer integer) {
                subscriber.onNext("" + integer);
            }

            @Override
            public void onCompleted() {
                subscriber.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                subscriber.onError(e);
            }
        };
    }
});

讲述 lift() 的原理只是为了让你更好地了解 RxJava ,从而可以更好地使用它。然而不管你是否理解了 lift() 的原理,RxJava 都不建议开发者自定义 Operator 来直接使用 lift(),而是建议尽量使用已有的 lift() 包装方法(如 map() flatMap() 等)进行组合来实现需求,因为直接使用 lift() 非常容易发生一些难以发现的错误。

3) compose: 对 Observable 整体的变换

除了 lift() 之外, Observable 还有一个变换方法叫做 compose(Transformer)。它和 lift() 的区别在于, lift() 是针对事件项和事件序列的,而 compose() 是针对 Observable 自身进行变换。举个例子,假设在程序中有多个 Observable ,并且他们都需要应用一组相同的 lift() 变换。你可以这么写:

observable1
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber1);
observable2
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber2);
observable3
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber3);
observable4
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber1);

你觉得这样太不软件工程了,于是你改成了这样:

private Observable liftAll(Observable observable) {
    return observable
        .lift1()
        .lift2()
        .lift3()
        .lift4();
}
...
liftAll(observable1).subscribe(subscriber1);
liftAll(observable2).subscribe(subscriber2);
liftAll(observable3).subscribe(subscriber3);
liftAll(observable4).subscribe(subscriber4);

Readability, maintainability have increased. But Observablebe wrapped up one way, this way for Observaleflexibility seems to add a point so limited. How to do? This time, it should compose()be solved:

public class LiftAllTransformer implements Observable.Transformer<Integer, String> {
    @Override
    public Observable<String> call(Observable<Integer> observable) {
        return observable
            .lift1()
            .lift2()
            .lift3()
            .lift4();
    }
}
...
Transformer liftAll = new LiftAllTransformer();
observable1.compose(liftAll).subscribe(subscriber1);
observable2.compose(liftAll).subscribe(subscriber2);
observable3.compose(liftAll).subscribe(subscriber3);
observable4.compose(liftAll).subscribe(subscriber4);

Thus as above, using the compose()method, Observablemay be utilized incoming Transformerobject callmethod directly process itself, it need not be in the interior of the method.
Original link https://gank.io/post/560e15be2dca930e00da1083#toc_17
Ali P7 mobile Internet architect, Advanced Video (Daily update) for free learning, please click: https://space.bilibili.com/474380680

Guess you like

Origin www.cnblogs.com/Android-Alvin/p/12102943.html