Rxjava 2.0 tutorial for beginners (4)

Keywords: Android

Preface

In the previous section, we mentioned Flowable and Backpress Backpressure. Originally, this section really wanted to talk about these two things, but half of them still feel bad and the time is not yet right. So, here is a preparation work. First, let's take you to learn the zip operator. This operator is also a more compelling thing, involving more things, mainly some details of things too much, by learning this operator, we can pave the way for the next section of Backpressure.

Topic

As usual, let's stick a more formal explanation first.

Zip combines events sent by Observable through a function, and then sends events combined. It applies the function in strict order. It only emits as much data as the Observable that emits the fewest data items.

Let's explain it with easy-to-understand pictures.


zip.png

As you can see from this picture, this upstream is different from the previous one. We have two water pipes.

One pipe is responsible for sending circular events and the other pipe is responsible for sending triangular events. Through the Zip operator, circular events and triangular events are merged into a rectangular event.

Let's look at the decomposition action again.


zip1.png

Through the decomposition action, we can see that:

  • The process of combination is to extract one event from each of the two pipes for combination. And an event can only be used once. The order of composition is strictly in accordance with the smooth sending of events. That is to say, there will be no round 1 event and triangle B event to merge. It is also impossible to merge round 2 and triangle A.
  • The number of eventual downstream events received is the same as the number of events in the upstream pipe that sent the fewest events. This is also very understandable, because an event is taken from each pipe to merge, and at least that must be taken first. At this time, although there are still incidents in other pipes, there are not enough incidents to combine, so the downstream will not receive the remaining incidents.

After analyzing the general principle, we should combine work with leisure. First, let's see how to write the actual code.

Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {            
    @Override                                                                                         
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {                      
        Log.d(TAG, "emit 1");                                                                         
        emitter.onNext(1);                                                                            
        Log.d(TAG, "emit 2");                                                                         
        emitter.onNext(2);                                                                            
        Log.d(TAG, "emit 3");                                                                         
        emitter.onNext(3);                                                                            
        Log.d(TAG, "emit 4");                                                                         
        emitter.onNext(4);                                                                            
        Log.d(TAG, "emit complete1");                                                                 
        emitter.onComplete();                                                                         
    }                                                                                                 
});                                                                   

Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {              
    @Override                                                                                         
    public void subscribe(ObservableEmitter<String> emitter) throws Exception {                       
        Log.d(TAG, "emit A");                                                                         
        emitter.onNext("A");                                                                          
        Log.d(TAG, "emit B");                                                                         
        emitter.onNext("B");                                                                          
        Log.d(TAG, "emit C");                                                                         
        emitter.onNext("C");                                                                          
        Log.d(TAG, "emit complete2");                                                                 
        emitter.onComplete();                                                                         
    }                                                                                                 
});                                                                     

Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {                  
    @Override                                                                                         
    public String apply(Integer integer, String s) throws Exception {                                 
        return integer + s;                                                                           
    }                                                                                                 
}).subscribe(new Observer<String>() {                       
    @Override                                                                                         
    public void onSubscribe(Disposable d) {                                                           
        Log.d(TAG, "onSubscribe");                                                                    
    }                                                                                                 

    @Override                                                                                         
    public void onNext(String value) {                                                                
        Log.d(TAG, "onNext: " + value);                                                               
    }                                                                                                 

    @Override                                                                                         
    public void onError(Throwable e) {                                                                
        Log.d(TAG, "onError");                                                                        
    }                                                                                                 

    @Override                                                                                         
    public void onComplete() {                                                                        
        Log.d(TAG, "onComplete");                                                                     
    }                                                                                                 
});

We created two upstream pipes, one sending 1,2,3,4,Complete, and the other sending A,B,C,Complete. Then we combined the emitted events with Zip to see the results of the operation.

D/TAG: onSubscribe     
D/TAG: emit 1          
D/TAG: emit 2          
D/TAG: emit 3          
D/TAG: emit 4          
D/TAG: emit complete1  
D/TAG: emit A          
D/TAG: onNext: 1A      
D/TAG: emit B          
D/TAG: onNext: 2B      
D/TAG: emit C          
D/TAG: onNext: 3C      
D/TAG: emit complete2  
D/TAG: onComplete

The result seems to be right.. But there's always something wrong with it.

What's wrong? Why does it feel like the second pipe doesn't start sending until it's finished? Is it right? Let's verify it.

Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {           
    @Override                                                                                        
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {                     
        Log.d(TAG, "emit 1");                                                                        
        emitter.onNext(1);                                                                           
        Thread.sleep(1000);                                                                          

        Log.d(TAG, "emit 2");                                                                        
        emitter.onNext(2);                                                                           
        Thread.sleep(1000);                                                                          

        Log.d(TAG, "emit 3");                                                                        
        emitter.onNext(3);                                                                           
        Thread.sleep(1000);                                                                          

        Log.d(TAG, "emit 4");                                                                        
        emitter.onNext(4);                                                                           
        Thread.sleep(1000);                                                                          

        Log.d(TAG, "emit complete1");                                                                
        emitter.onComplete();                                                                        
    }                                                                                                
});                                                                                                  

Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {             
    @Override                                                                                        
    public void subscribe(ObservableEmitter<String> emitter) throws Exception {                      
        Log.d(TAG, "emit A");                                                                        
        emitter.onNext("A");                                                                         
        Thread.sleep(1000);                                                                          

        Log.d(TAG, "emit B");                                                                        
        emitter.onNext("B");                                                                         
        Thread.sleep(1000);                                                                          

        Log.d(TAG, "emit C");                                                                        
        emitter.onNext("C");                                                                         
        Thread.sleep(1000);                                                                          

        Log.d(TAG, "emit complete2");                                                                
        emitter.onComplete();                                                                        
    }                                                                                                
});                                                                                                  

Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {                 
    @Override                                                                                        
    public String apply(Integer integer, String s) throws Exception {                                
        return integer + s;                                                                          
    }                                                                                                
}).subscribe(new Observer<String>() {                                                                
    @Override                                                                                        
    public void onSubscribe(Disposable d) {                                                          
        Log.d(TAG, "onSubscribe");                                                                   
    }                                                                                                

    @Override                                                                                        
    public void onNext(String value) {                                                               
        Log.d(TAG, "onNext: " + value);                                                              
    }                                                                                                

    @Override                                                                                        
    public void onError(Throwable e) {                                                               
        Log.d(TAG, "onError");                                                                       
    }                                                                                                

    @Override                                                                                        
    public void onComplete() {                                                                       
        Log.d(TAG, "onComplete");                                                                    
    }                                                                                                
});

This time, we added a second delay after each event was sent. Let's see the results. Note that this is a GIF diagram:


zip.gif

I'm afraid you can't see clearly. I've changed the old font on purpose.

Assy, it seems that the pipes that are sent first are pipes that are sent again and again. Why is this happening? Because both pipes are running in the same thread, and there must be a sequence of code execution in the same thread.

So let's make a slight change to prevent them from switching threads on the same thread. Turn around and look at the previous sections.

Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {         
    @Override                                                                                      
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {                   
        Log.d(TAG, "emit 1");                                                                      
        emitter.onNext(1);                                                                         
        Thread.sleep(1000);                                                                        

        Log.d(TAG, "emit 2");                                                                      
        emitter.onNext(2);                                                                         
        Thread.sleep(1000);                                                                        

        Log.d(TAG, "emit 3");                                                                      
        emitter.onNext(3);                                                                         
        Thread.sleep(1000);                                                                        

        Log.d(TAG, "emit 4");                                                                      
        emitter.onNext(4);                                                                         
        Thread.sleep(1000);                                                                        

        Log.d(TAG, "emit complete1");                                                              
        emitter.onComplete();                                                                      
    }                                                                                              
}).subscribeOn(Schedulers.io());                                                                   

Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {           
    @Override                                                                                      
    public void subscribe(ObservableEmitter<String> emitter) throws Exception {                    
        Log.d(TAG, "emit A");                                                                      
        emitter.onNext("A");                                                                       
        Thread.sleep(1000);                                                                        

        Log.d(TAG, "emit B");                                                                      
        emitter.onNext("B");                                                                       
        Thread.sleep(1000);                                                                        

        Log.d(TAG, "emit C");                                                                      
        emitter.onNext("C");                                                                       
        Thread.sleep(1000);                                                                        

        Log.d(TAG, "emit complete2");                                                              
        emitter.onComplete();                                                                      
    }                                                                                              
}).subscribeOn(Schedulers.io());                                                                   

Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {               
    @Override                                                                                      
    public String apply(Integer integer, String s) throws Exception {                              
        return integer + s;                                                                        
    }                                                                                              
}).subscribe(new Observer<String>() {                    
    @Override                                                                                      
    public void onSubscribe(Disposable d) {                                                        
        Log.d(TAG, "onSubscribe");                                                                 
    }                                                                                              

    @Override                                                                                      
    public void onNext(String value) {                                                             
        Log.d(TAG, "onNext: " + value);                                                            
    }                                                                                              

    @Override                                                                                      
    public void onError(Throwable e) {                                                             
        Log.d(TAG, "onError");                                                                     
    }                                                                                              

    @Override                                                                                      
    public void onComplete() {                                                                     
        Log.d(TAG, "onComplete");                                                                  
    }                                                                                              
});

Okay, let's have the pipes send events in the IO thread this time. Let's see the results again.

D/TAG: onSubscribe    
D/TAG: emit A         
D/TAG: emit 1         
D/TAG: onNext: 1A     
D/TAG: emit B         
D/TAG: emit 2         
D/TAG: onNext: 2B     
D/TAG: emit C         
D/TAG: emit 3         
D/TAG: onNext: 3C     
D/TAG: emit complete2 
D/TAG: onComplete

GIF diagram:


zip_io.gif

Ah! That's right. Two pipes start sending at the same time. Zip combines one for each one, and then sends the combined results downstream.

No! Perhaps a more careful friend can see the beginning again. The first water pipe sent four data + one Complete, which was clearly still there before. Why did it disappear here?

This is because as we said before, the number of events sent by zip is related to the number of events sent by the least incident pipe upstream. In this case, our second pipe only sends three events and then sends Complete, even though the first pipe still has Event 4 and Event Complete not sent. But what's the point if they don't send? So in the spirit of saving is virtue, they simply break their dog's legs and don't let it send.

As for why the preceding example was sent, it was not already said that it was! In! Same! One! One! Line! Cheng! Li! Is it!!!! Ask Lao Tzu to kill you again!

Good programmers may ask again, so I don't send Complete? The answer is obvious, upstream will continue to send events, but downstream will still not receive those superfluous events. Believe it or not, you can try.

practice

After learning Zip's basic usage, what's the use of Zip in Android? In fact, Zip can be used in many scenarios, for example.

For example, an interface needs to display some information of the user, which is obtained from two server interfaces respectively, and can only be displayed when both are obtained. Zip can be used at this time:

First, the two request interfaces are defined separately:

public interface Api {
    @GET
    Observable<UserBaseInfoResponse> getUserBaseInfo(@Body UserBaseInfoRequest request);

    @GET
    Observable<UserExtraInfoResponse> getUserExtraInfo(@Body UserExtraInfoRequest request);

}

Then Zip is used to package the request:

Observable<UserBaseInfoResponse> observable1 =                                            
        api.getUserBaseInfo(new UserBaseInfoRequest()).subscribeOn(Schedulers.io());      

Observable<UserExtraInfoResponse> observable2 =                                           
        api.getUserExtraInfo(new UserExtraInfoRequest()).subscribeOn(Schedulers.io());    

Observable.zip(observable1, observable2,                                                  
        new BiFunction<UserBaseInfoResponse, UserExtraInfoResponse, UserInfo>() {         
            @Override                                                                     
            public UserInfo apply(UserBaseInfoResponse baseInfo,                          
                                  UserExtraInfoResponse extraInfo) throws Exception {     
                return new UserInfo(baseInfo, extraInfo);                                 
            }                                                                             
        }).observeOn(AndroidSchedulers.mainThread())                                      
        .subscribe(new Consumer<UserInfo>() {                                             
            @Override                                                                     
            public void accept(UserInfo userInfo) throws Exception {                      
                //do something;                                                           
            }                                                                             
        });

Posted by afhouston on Fri, 22 Mar 2019 13:51:53 -0700