RxJava2.0 (7) BackpressureStrategy of Flowable

In the previous section, we learned some basic knowledge of FLowable, and also dug a lot of holes. Let us fill in the holes in this section.

topic

At the end of the last section, we had an example. When the upstream sends 128 events at a time, there is no problem. Once the number exceeds 128, an exception will be thrown to MissingBackpressureExceptionremind you that the upstream has sent too many events, and the downstream cannot handle them. So how to solve it?

Let's think about it first. There is no problem sending 128 events because FLowablethere is a water tank with a size of 128 inside. If it exceeds 128, it will overflow. Since your water tank is so small, how about I change it for you? Listen 大水缸. It looks very reasonable, let's try:

Flowable.create(new FlowableOnSubscribe<Integer>() {
     @Override
     public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
         for (int i = 0; i < 1000; i++) {
              Log.d(TAG, "emit " + i);
              emitter.onNext(i);
         }
     }
}, BackpressureStrategy.BUFFER)  // 换个大大的水缸
   .subscribeOn(Schedulers.io())
   .observeOn(AndroidSchedulers.mainThread())
   .subscribe(new Subscriber<Integer>() {

        @Override
        public void onSubscribe(Subscription s) {
             Log.d(TAG, "onSubscribe");
             mSubscription = s;
        }

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

        @Override
        public void onError(Throwable t) {
             Log.w(TAG, "onError: ", t);
        }

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

This time we directly let the upstream send 1000 events, and the downstream still does not call request to request. Unlike before, the strategy we used this time is, BackpressureStrategy.BUFFERthis is ours 新水缸, this water tank is better than the original water tank It’s too much, if the original water tank is a 95-type rifle, then this new water tank is like a gold AK, it has no size limit, so it can store many events.

So the result of this operation is:

zlc.season.rxjava2demo D/TAG: onSubscribe
zlc.season.rxjava2demo D/TAG: emit 0
zlc.season.rxjava2demo D/TAG: emit 1
zlc.season.rxjava2demo D/TAG: emit 2
...
zlc.season.rxjava2demo D/TAG: emit 997
zlc.season.rxjava2demo D/TAG: emit 998
zlc.season.rxjava2demo D/TAG: emit 999

Have you noticed that the FLowable and Observable seem to be the same after changing the water tank...

Yes, the characteristics of FLowable at this time are exactly the same as those of Observable. Therefore, if you simply use FLowable like this , you also need to pay attention to the OOM problem , such as the following example:

Flowable.create(new FlowableOnSubscribe<Integer>() {
     @Override
     public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
          for (int i = 0; ; i++) {
               emitter.onNext(i);
          }
     }
}, BackpressureStrategy.BUFFER)
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Subscriber<Integer>() {

       @Override
       public void onSubscribe(Subscription s) {
           Log.d(TAG, "onSubscribe");
           mSubscription = s;
       }

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

       @Override
       public void onError(Throwable t) {
           Log.w(TAG, "onError: ", t);
       }

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

It can also be seen that the memory grows rapidly until OOM is thrown at the end. So don't be obsessed with FLowable.

Some friends may have noticed that when using Observable to test before, the memory growth was very rapid, and it was OOM in a few seconds, but the growth rate here is relatively slow. You can go back and look at the GIF in the previous article for comparison. This also shows that Compared with Observable, FLowable has some shortcomings in terms of performance. After all, FLowable has done more operations to achieve responsive pull, and performance loss is inevitable. Therefore, just because FLowable is a new product, it is also blind to use No, we need to divide the scene into specific situations,

So besides changing FLowable to a larger water tank, is there any other way, because a bigger water tank is just a way to delay the attack, and OOM will show you if it doesn’t move.

Think about how to solve the problem of sending events upstream too fast when we learned about Observable. There is a trick called 数量winning from the top. The same method is also available in FLowable, which corresponds to BackpressureStrategy.DROPthese BackpressureStrategy.LATESTtwo strategies.

You can guess what they are for from the name. Drop is to directly discard the events that cannot be saved, and Latest is to keep only the latest events. Let’s take a look at their actual effects. Let’s take a look at Drop first
:

public static void request() {
        mSubscription.request(128);
    }

public static void demo3() {
        Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                for (int i = 0; ; i++) {
                    emitter.onNext(i);
                }
            }
        }, BackpressureStrategy.DROP).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                    }

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

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

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

We still allow the upstream to send events in an infinite loop. This time the strategy is to choose Drop, and at the same time save the Subscription. When we call request(128) externally, we can see the result of the operation.

Let's first guess the running result. Why is request(128) here? Because it has already been said before that the default water tank size inside FLowable is 128. Therefore, it will definitely put 128 events of 0-127 at the beginning Save it, and then discard the rest of the events. When we request (128), the downstream will process the 128 events, then the upstream water tank will reload the new 128 events, and so on. Take a look at the results of the operation:

From the running results, we can see that this is indeed the case. During the first request, the downstream did receive 128 events from 0 to 127, but it is not sure during the second request, because the upstream has been sending events . The memory usage is also normal, I believe everyone knows the function of drop.

Let's take a look at Latest again:
 

public static void request() {
        mSubscription.request(128);
    }

public static void demo4() {
        Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                for (int i = 0; ; i++) {
                    emitter.onNext(i);
                }
            }
        }, BackpressureStrategy.LATEST).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                    }

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

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

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

Similarly, the upstream infinitely loops to send events, the strategy selects Latest, and saves the Subscription at the same time, so that it is convenient to call request(128) externally. Let’s take a look at the running results this time:

Hey, it seems to be similar to Drop. Latest also saves 128 events from 0-127 first, and waits for the downstream to process these 128 events before performing subsequent processing. I can’t see any difference from here. ..

Let's improve the above two pieces of code, let's take a look at the improved version of DROP first:

 Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                for (int i = 0; i < 10000; i++) {  //只发1w个事件
                    emitter.onNext(i);
                }
            }
        }, BackpressureStrategy.DROP).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(128);  //一开始就处理掉128个事件
                    }

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

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

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

There are two differences between this code and the previous one. One is that the upstream only sent 10,000 events, and the other is that the downstream immediately processed 128 events at the beginning. Then we try to call request(128) externally to see Look at the running results:

This time, we can see that 128 events were processed downstream at the beginning, and when we requested again, we only got the 3317th event, and the subsequent events were directly discarded.

Let's take a look at the latest running results:

As can be seen from the running results, except for the first 128 events, unlike Drop, Latest can always get the latest event, for example, here we can always get the last event 9999.

Well, we have finished talking about the FLowable strategy. Some friends want to ask, these FLowables are created by myself, so I can choose a strategy. What should I do when faced with some FLowables that I did not create by myself? For example The interval operator in RxJava, this operator is not created by ourselves, let's take a look at the following example:

Flowable.interval(1, TimeUnit.MICROSECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Long>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(Long.MAX_VALUE);
                    }

                    @Override
                    public void onNext(Long aLong) {
                        Log.d(TAG, "onNext: " + aLong);
                        try {
                            Thread.sleep(1000);  //延时1秒
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

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

The interval operator sends Long-type events, starting from 0, adding 1 to the number and sending it out every specified time. In this example, we let it send an event every 1 millisecond, and delay it downstream by 1 second To receive processing, you don't need to guess what the result is:

zlc.season.rxjava2demo D/TAG: onSubscribe
zlc.season.rxjava2demo W/TAG: onError: 
                              io.reactivex.exceptions.MissingBackpressureException: Can't deliver value 128 due to lack of requests
                                  at io.reactivex.internal.operators.flowable.FlowableInterval$IntervalSubscriber.run(FlowableInterval.java:87)
                                  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
                                  at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:278)
                                  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:273)
                                  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
                                  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
                                  at java.lang.Thread.run(Thread.java:761)

MissingBackpressureExceptionAn exception was thrown as soon as it was running , reminding us that there are too many posts, so what should we do, this is not the FLowable we created ourselves...

Don't panic, although we didn't create it ourselves, RxJava provides us with other methods:

  • onBackpressureBuffer()
  • onBackpressureDrop()
  • onBackpressureLatest()

Are you familiar with it? This is the same strategy as we learned above, and the usage is also simple. Take the example just now and use it now:

Flowable.interval(1, TimeUnit.MICROSECONDS)
                .onBackpressureDrop()  //加上背压策略
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Long>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(Long.MAX_VALUE);
                    }

                    @Override
                    public void onNext(Long aLong) {
                        Log.d(TAG, "onNext: " + aLong);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

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

I won't list the rest one by one.

Well, that's all for today's tutorial. In this section, we learned how to use the built-in BackpressureStrategy to solve the problem of unbalanced upstream and downstream event rates. These strategies were actually mentioned when we mentioned Observable before, but they are quite different. Poor, as long as you understand why the upstream event is too fast and the downstream processing is too slow, you can handle it easily. FLowable is nothing more than packaging it for you. It is indeed friendly to beginners, but many beginners often only know How , but don’t know Why, the most important thing is to know why, not how.

(Most of the rest of the tutorials end here, but, do you think that FLowable is just such a thing? You are wrong, we will continue in the next section.....

Guess you like

Origin blog.csdn.net/m0_49508485/article/details/126994154