RxJava线程切换和背压策略

一、线程切换

1.理解线程切换原理有什么意义?
  • 可以清楚的知道这个线程切换操作会影响到哪些代码的执行线程,不会影响到哪些代码的执行线程
  • 灵活运用线程切换来实现复杂的应用场景
  • 有利于在发生线程相关的问题时进行调试
2.实现方式

采用 RxJava内置的线程调度器( Scheduler ),即通过 功能性操作符subscribeOn() & observeOn()实现

RxJava 遵循的是线程不变的原则
在哪个线程调用 subscribe(),就在哪个线程生产事件( onSubscribe.call() )和消费事件(subscriber.onNext())

在 RxJava中,内置了多种用于调度的线程类型

类型含义应用场景
Schedulers.immediate()当前线程 = 不指定线程默认
AndroidSchedulers.mainThread()Android主线程操作UI
Schedulers.newThread()常规新线程耗时等操作
Schedulers.io()io操作线程网络请求、读写文件等io密集型操作
Schedulers.computation()CPU计算操作线程大量计算操作

注:RxJava内部使用 线程池 来维护这些线程,所以线程的调度效率非常高。

举个例子
由 id 取得图片并显示
由指定的一个 drawable 文件 id drawableRes 取得图片,并显示在 ImageView 中,并在出现异常的时候打印 Toast 报错:

int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
    @Override
    public void call(Subscriber<? super Drawable> subscriber) {
        Drawable drawable = getTheme().getDrawable(drawableRes));
        subscriber.onNext(drawable);
        subscriber.onCompleted();
    }
}).subscribe(new Observer<Drawable>() {
    @Override
    public void onNext(Drawable drawable) {
        imageView.setImageDrawable(drawable);
    }

@Override
public void onCompleted() {
}

@Override
public void onError(Throwable e) {
    Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});

subscribeOn() 和 observeOn()具体实现

int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
        @Override
        public void call(Subscriber<? super Drawable> subscriber) {
            Drawable drawable = getTheme().getDrawable(drawableRes));
            subscriber.onNext(drawable);
            subscriber.onCompleted();
        }
    })
    .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
    .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
    .subscribe(new Observer<Drawable>() {
        @Override
        public void onNext(Drawable drawable) {
            imageView.setImageDrawable(drawable);
        }
    @Override
    public void onCompleted() {
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
    }
});

subscribeOn() 控制事件产生的线程,即onSubscribe.call()执行的线程
observeOn()控制事件消费的线程,即(subscriber.onNext())执行的线程

特别注意:
1.若Observable.subscribeOn()多次指定被观察者 生产事件的线程,则只有第一次指定有效,其余的指定线程无效

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Rxjava";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 步骤1:创建被观察者 Observable & 发送事件
        // 在主线程创建被观察者 Observable 对象
        // 所以生产事件的线程是:主线程

        Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {

                Log.d(TAG, " 被观察者 Observable的工作线程是: " + Thread.currentThread().getName());
                // 打印验证
                emitter.onNext(1);
                emitter.onComplete();
            }
        });

    // 步骤2:创建观察者 Observer 并 定义响应事件行为
            // 在主线程创建观察者 Observer 对象
            // 所以接收 & 响应事件的线程是:主线程
            Observer<Integer> observer = new Observer<Integer>() {

            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "开始采用subscribe连接");
                Log.d(TAG, " 观察者 Observer的工作线程是: " + Thread.currentThread().getName());
                // 打印验证

            }
            @Override
            public void onNext(Integer value) {
                Log.d(TAG, "对Next事件"+ value +"作出响应"  );
            }
            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "对Error事件作出响应");
            }
            @Override
            public void onComplete() {
                Log.d(TAG, "对Complete事件作出响应");
            }
        };

        // 步骤3:通过订阅(subscribe)连接观察者和被观察者
         observable.subscribeOn(Schedulers.newThread()) // 第一次指定被观察者线程 = 新线程
              .subscribeOn(AndroidSchedulers.mainThread()) // 第二次指定被观察者线程 = 主线程
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe(observer);
    }
}

测试结果:被观察者的线程 = 第一次指定的线程 = 新的工作线程,第二次指定的线程(主线程)无效
在这里插入图片描述
2. 若Observable.observeOn()多次指定观察者 接收 & 响应事件的线程,则每次指定均有效,即每指定一次,就会进行一次线程的切换

// 步骤3:通过订阅(subscribe)连接观察者和被观察者
        observable.subscribeOn(Schedulers.newThread())
                  .observeOn(AndroidSchedulers.mainThread()) // 第一次指定观察者线程 = 主线程
                  .doOnNext(new Consumer<Integer>() { // 生产事件
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d(TAG, "第一次观察者Observer的工作线程是: " + Thread.currentThread().getName());
                    }
                })
                .observeOn(Schedulers.newThread()) // 第二次指定观察者线程 = 新的工作线程
                .subscribe(observer); // 生产事件


// 注:
// 1. 整体方法调用顺序:观察者.onSubscribe()> 被观察者.subscribe()> 观察者.doOnNext()>观察者.onNext()>观察者.onComplete() 
// 2. 观察者.onSubscribe()固定在主线程进行

测试结果:每调用一次observeOn(),观察者的线程就会切换一次
在这里插入图片描述

二、背压

1 问题

被观察者 发送事件速度太快,而观察者 来不及接收所有事件,从而导致观察者无法及时响应 / 处理所有发送过来事件的问题,最终导致缓存区溢出、事件丢失 & OOM

  1. 如,点击按钮事件:连续过快的点击按钮10次,则只会造成点击2次的效果;
  2. 解释:因为点击速度太快了,所以按钮来不及响应

下面再举个例子:

被观察者的发送事件速度 = 10ms / 个
观察者的接收事件速度 = 5s / 个

即出现发送 & 接收事件严重不匹配的问题

Observable.create(new ObservableOnSubscribe<Integer>() {
            // 1. 创建被观察者 & 生产事件
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {

            for (int i = 0; ; i++) {
                Log.d(TAG, "发送了事件"+ i );
                Thread.sleep(10);
                // 发送事件速度:10ms / 个 
                emitter.onNext(i);

            }
            
        }
    }).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
            .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
         .subscribe(new Observer<Integer>() {
        // 2. 通过通过订阅(subscribe)连接观察者和被观察者
             
        @Override
        public void onSubscribe(Disposable d) {
            Log.d(TAG, "开始采用subscribe连接");
        }

        @Override
        public void onNext(Integer value) {

            try {
                // 接收事件速度:5s / 个 
                Thread.sleep(5000);
                Log.d(TAG, "接收到了事件"+ value  );
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onError(Throwable e) {
            Log.d(TAG, "对Error事件作出响应");
        }

        @Override
        public void onComplete() {
            Log.d(TAG, "对Complete事件作出响应");
        }

    });

结果

由于被观察者发送事件速度 > 观察者接收事件速度,所以出现流速不匹配问题,从而导致OOM

那么我们改如何解决呢?下面我们就是要讲的 采用 背压策略。

2. 背压策略简介
2.1 定义

一种 控制事件流速 的策略

2.2 作用

在 异步订阅关系 中,控制事件发送 & 接收的速度

注:背压的作用域 = 异步订阅关系,即 被观察者 & 观察者处在不同线程中

2.3 解决的问题

解决了 因被观察者发送事件速度 与 观察者接收事件速度 不匹配(一般是前者 快于 后者),从而导致观察者无法及时响应 / 处理所有 被观察者发送事件 的问题

2.4 应用场景
  • 被观察者发送事件速度 与 观察者接收事件速度 不匹配的场景
  • 具体场景就取决于该事件的类型,如:网络请求,那么具体场景:有很多网络请求需要执行,但执行者的执行速度没那么快,此时就需要使用背压策略来进行控制。
3. 背压策略的原理

那么,RxJava实现背压策略(Backpressure)的原理是什么呢?
解决方案 & 思想主要如下:

在这里插入图片描述

4. 背压策略的具体实现:Flowable
4.1 Flowable 特点

在这里插入图片描述

4.2 Flowable的基础使用

Flowable的基础使用非常类似于 Observable

// 步骤1:创建被观察者 =  Flowable
        Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                Log.d(TAG, "发送事件 1");
                emitter.onNext(1);
                Log.d(TAG, "发送事件 2");
                emitter.onNext(2);
                Log.d(TAG, "发送事件 3");
                emitter.onNext(3);
                Log.d(TAG, "发送完成");
                emitter.onComplete();
            }
        }, BackpressureStrategy.ERROR)
                .subscribe(new Subscriber<Integer>() {
                // 步骤2:创建观察者 =  Subscriber & 建立订阅关系
                @Override
                public void onSubscribe(Subscription s) {
                    Log.d(TAG, "onSubscribe");
                    s.request(3);
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述

5.1控制 观察者接收事件 的速度
  • 观察者 & 被观察者 之间存在2种订阅关系:同步 & 异步。
类型说明
同步订阅1.观察者&被观察者 工作在同一线程;
2.被观察者没发一条事件必须等到观察者
接收&处理后才能继续发送下一个事件
异步订阅1.观察者&被观察者 工作在不同线程;
2.被观察者不需要等待观察者接收&处理后
才能发送下一个事件,而是不断发送,直到发送事件完毕
(但此时的事件并不会直接发送到观察者处,而是先发送到缓存区,
等观察者从缓存区取出事件来处理)
  • 对于异步订阅关系,存在 被观察者发送事件速度 与观察者接收事件速度 不匹配的情况
  1. 发送 & 接收事件速度 = 单位时间内 发送&接收事件的数量
  2. 大多数情况,主要是 被观察者发送事件速度 > 观察者接收事件速度
5.1.1 异步订阅情况

简介:
在这里插入图片描述
具体原理图

具体使用:

// 1. 创建被观察者Flowable
        Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                // 一共发送4个事件
                Log.d(TAG, "发送事件 1");
                emitter.onNext(1);
                Log.d(TAG, "发送事件 2");
                emitter.onNext(2);
                Log.d(TAG, "发送事件 3");
                emitter.onNext(3);
                Log.d(TAG, "发送事件 4");
                emitter.onNext(4);
                Log.d(TAG, "发送完成");
                emitter.onComplete();
            }
        }, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        // 对比Observer传入的Disposable参数,Subscriber此处传入的参数 = Subscription
                        // 相同点:Subscription参数具备Disposable参数的作用,即Disposable.dispose()切断连接, 同样的调用Subscription.cancel()切断连接
                        // 不同点:Subscription增加了void request(long n)

                    s.request(3);
                    // 作用:决定观察者能够接收多少个事件
                    // 如设置了s.request(3),这就说明观察者能够接收3个事件(多出的事件存放在缓存区)
                    // 官方默认推荐使用Long.MAX_VALUE,即s.request(Long.MAX_VALUE);
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述
有2个结论是需要大家注意的
在这里插入图片描述

当缓存区存满时(128个事件)溢出报错的原理图

代码演示1:观察者不接收事件的情况下,被观察者继续发送事件 & 存放到缓存区;再按需取出

/**
    * 步骤1:设置变量
    */
    private static final String TAG = "Rxjava";
    private Button btn; // 该按钮用于调用Subscription.request(long n )
    private Subscription mSubscription; // 用于保存Subscription对象
    
  /**
    * 步骤2:设置点击事件 = 调用Subscription.request(long n )
    */
        btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSubscription.request(2);
            }

        });

    /**
     * 步骤3:异步调用
     */
    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            Log.d(TAG, "发送事件 1");
            emitter.onNext(1);
            Log.d(TAG, "发送事件 2");
            emitter.onNext(2);
            Log.d(TAG, "发送事件 3");
            emitter.onNext(3);
            Log.d(TAG, "发送事件 4");
            emitter.onNext(4);
            Log.d(TAG, "发送完成");
            emitter.onComplete();
        }
    }, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
            .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    Log.d(TAG, "onSubscribe");
                    mSubscription = s;
                    // 保存Subscription对象,等待点击按钮时(调用request(2))观察者再接收事件
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述

代码演示2:观察者不接收事件的情况下,被观察者继续发送事件至超出缓存区大小(128)

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                // 一共发送129个事件,即超出了缓存区的大小
                for (int i = 0;i< 129; i++) {
                    Log.d(TAG, "发送了事件" + i);
                    emitter.onNext(i);
                }
                emitter.onComplete();
            }
        }, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        // 默认不设置可接收事件大小
                    }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述

5.1 .2 同步订阅情况

被观察者在发送1个事件后,必须等待观察者接收后,才能继续发下1个事件

/**
         * 步骤1:创建被观察者 =  Flowable
         */
        Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            
            // 发送3个事件
            Log.d(TAG, "发送了事件1");
            emitter.onNext(1);
            Log.d(TAG, "发送了事件2");
            emitter.onNext(2);
            Log.d(TAG, "发送了事件3");
            emitter.onNext(3);
            emitter.onComplete();
        }
    }, BackpressureStrategy.ERROR);

    /**
     * 步骤2:创建观察者 =  Subscriber
     */
    Subscriber<Integer> downstream = new Subscriber<Integer>() {

        @Override
        public void onSubscribe(Subscription s) {
            Log.d(TAG, "onSubscribe");
             s.request(3);
             // 每次可接收事件 = 3 二次匹配
        }

        @Override
        public void onNext(Integer integer) {
            Log.d(TAG, "接收到了事件 " + integer);
        }

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

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

    /**
     * 步骤3:建立订阅关系
     */
    upstream.subscribe(downstream);

在这里插入图片描述

以,实际上并不会出现被观察者发送事件速度 > 观察者接收事件速度的情况。可是,却会出现被观察者发送事件数量 > 观察者接收事件数量的问题。

如:观察者只能接受3个事件,但被观察者却发送了4个事件,所以出现了不匹配情况

/**
         * 步骤1:创建被观察者 =  Flowable
         */
        Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {

            // 被观察者发送事件数量 = 4个
            Log.d(TAG, "发送了事件1");
            emitter.onNext(1);
            Log.d(TAG, "发送了事件2");
            emitter.onNext(2);
            Log.d(TAG, "发送了事件3");
            emitter.onNext(3);
            Log.d(TAG, "发送了事件4");
            emitter.onNext(4);
            emitter.onComplete();
        }
    }, BackpressureStrategy.ERROR);

    /**
     * 步骤2:创建观察者 =  Subscriber
     */
    Subscriber<Integer> downstream = new Subscriber<Integer>() {

        @Override
        public void onSubscribe(Subscription s) {
            Log.d(TAG, "onSubscribe");
             s.request(3);
             // 观察者接收事件 = 3个 ,即不匹配
        }

        @Override
        public void onNext(Integer integer) {
            Log.d(TAG, "接收到了事件 " + integer);
        }

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

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

    /**
     * 步骤3:建立订阅关系
     */
    upstream.subscribe(downstream);

在这里插入图片描述
所以,对于没有缓存区概念的同步订阅关系来说,单纯采用控制观察者的接收事件数量(响应式拉取)实际上就等于 “单相思”,虽然观察者控制了要接收3个事件,但假设被观察者需要发送4个事件,还是会出现问题。

有1个特殊情况需要注意
在这里插入图片描述

/**
  * 同步情况
  */
    /**
     * 步骤1:创建被观察者 =  Flowable
     */
    Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            Log.d(TAG, "发送了事件1");
            emitter.onNext(1);
            Log.d(TAG, "发送了事件2");
            emitter.onNext(2);
            Log.d(TAG, "发送了事件3");
            emitter.onNext(3);
            emitter.onComplete();
        }
    }, BackpressureStrategy.ERROR);

    /**
     * 步骤2:创建观察者 =  Subscriber
     */
    Subscriber<Integer> downstream = new Subscriber<Integer>() {

        @Override
        public void onSubscribe(Subscription s) {
            Log.d(TAG, "onSubscribe");
            // 不设置request(long n)
            // s.request(Long.MAX_VALUE);

        }

        @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");
        }
    };

    /**
     * 步骤3:建立订阅关系
     */
    upstream.subscribe(downstream);

在被观察者发送第1个事件后, 就抛出MissingBackpressureException异常 & 观察者没有收到任何事件

在这里插入图片描述

5.2 控制 被观察者发送事件 的速度

在这里插入图片描述
FlowableEmitter类的requested()介绍

public interface FlowableEmitter<T> extends Emitter<T> {
// FlowableEmitter = 1个接口,继承自Emitter
// Emitter接口方法包括:onNext(),onComplete() & onError

long requested();
// 作用:返回当前线程中request(a)中的a值
// 该request(a)则是措施1中讲解的方法,作用  = 设置

....// 仅贴出关键代码

}

每个线程中的requested()的返回值 = 该线程中的request(a)的a值

5.2.1 同步订阅情况

被观察者 通过 FlowableEmitter.requested()获得了观察者自身接收事件能力,从而根据该信息控制事件发送速度,从而达到了观察者反向控制被观察者的效果
具体使用
例子 : 被观察者根据观察者自身接收事件能力(10个事件),从而仅发送10个事件

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            
            // 调用emitter.requested()获取当前观察者需要接收的事件数量
            long n = emitter.requested();

            Log.d(TAG, "观察者可接收事件" + n);

            // 根据emitter.requested()的值,即当前观察者需要接收的事件数量来发送事件
            for (int i = 0; i < n; i++) {
                Log.d(TAG, "发送了事件" + i);
                emitter.onNext(i);
            }
        }
    }, BackpressureStrategy.ERROR)
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    Log.d(TAG, "onSubscribe");

                    // 设置观察者每次能接受10个事件
                    s.request(10);

                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述
在同步订阅情况中使用FlowableEmitter.requested()时,有以下几种使用特性需要注意的:
在这里插入图片描述

情况1:可叠加性
即:观察者可连续要求接收事件,被观察者会进行叠加并一起发送

Subscription.request(a1);
Subscription.request(a2);

代码实例:

  FlowableEmitter.requested()的返回值 = a1 + a2
Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                    
            // 调用emitter.requested()获取当前观察者需要接收的事件数量
            Log.d(TAG, "观察者可接收事件" + emitter.requested());

        }
    }, BackpressureStrategy.ERROR)
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    Log.d(TAG, "onSubscribe");

                    s.request(10); // 第1次设置观察者每次能接受10个事件
                    s.request(20); // 第2次设置观察者每次能接受20个事件

                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述

情况2:实时更新性
即,每次发送事件后,emitter.requested()会实时更新观察者能接受的事件

1.即一开始观察者要接收10个事件,发送了1个后,会实时更新为9个
2.仅计算Next事件,complete & error事件不算。

Subscription.request(10);
// FlowableEmitter.requested()的返回值 = 10

FlowableEmitter.onNext(1); // 发送了1个事件
// FlowableEmitter.requested()的返回值 = 9

代码演示

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {

            // 1. 调用emitter.requested()获取当前观察者需要接收的事件数量
            Log.d(TAG, "观察者可接收事件数量 = " + emitter.requested());

            // 2. 每次发送事件后,emitter.requested()会实时更新观察者能接受的事件
            // 即一开始观察者要接收10个事件,发送了1个后,会实时更新为9个
            Log.d(TAG, "发送了事件 1");
            emitter.onNext(1);
            Log.d(TAG, "发送了事件1后, 还需要发送事件数量 = " + emitter.requested());

            Log.d(TAG, "发送了事件 2");
            emitter.onNext(2);
            Log.d(TAG, "发送事件2后, 还需要发送事件数量 = " + emitter.requested());

            Log.d(TAG, "发送了事件 3");
            emitter.onNext(3);
            Log.d(TAG, "发送事件3后, 还需要发送事件数量 = " + emitter.requested());

            emitter.onComplete();
        }
    }, BackpressureStrategy.ERROR)
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    Log.d(TAG, "onSubscribe");

                    s.request(10); // 设置观察者每次能接受10个事件
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

情况3:异常

  • 当FlowableEmitter.requested()减到0时,则代表观察者已经不可接收事件
  • 此时被观察者若继续发送事件,则会抛出MissingBackpressureException异常

如观察者可接收事件数量 = 1,当被观察者发送第2个事件时,就会抛出异常

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {

            // 1. 调用emitter.requested()获取当前观察者需要接收的事件数量
            Log.d(TAG, "观察者可接收事件数量 = " + emitter.requested());

            // 2. 每次发送事件后,emitter.requested()会实时更新观察者能接受的事件
            // 即一开始观察者要接收10个事件,发送了1个后,会实时更新为9个
            Log.d(TAG, "发送了事件 1");
            emitter.onNext(1);
            Log.d(TAG, "发送了事件1后, 还需要发送事件数量 = " + emitter.requested());

            Log.d(TAG, "发送了事件 2");
            emitter.onNext(2);
            Log.d(TAG, "发送事件2后, 还需要发送事件数量 = " + emitter.requested());

            emitter.onComplete();
        }
    }, BackpressureStrategy.ERROR)
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {

                    Log.d(TAG, "onSubscribe");
                    s.request(1); // 设置观察者每次能接受1个事件

                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述

额外

  • 若观察者没有设置可接收事件数量,即无调用Subscription.request()
  • 那么被观察者默认观察者可接收事件数量 = 0,即FlowableEmitter.requested()的返回值 = 0
5.2.2 异步订阅情况

具体源码

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {

            // 调用emitter.requested()获取当前观察者需要接收的事件数量
            Log.d(TAG, "观察者可接收事件数量 = " + emitter.requested());

        }
    }, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
            .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    Log.d(TAG, "onSubscribe");
                    s.request(150);
                    // 该设置仅影响观察者线程中的requested,却不会影响的被观察者中的FlowableEmitter.requested()的返回值
                    // 因为FlowableEmitter.requested()的返回值 取决于RxJava内部调用request(n),而该内部调用会在一开始就调用request(128)
                    // 为什么是调用request(128)下面再讲解
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

在这里插入图片描述
在异步订阅关系中,反向控制的原理是:通过RxJava内部固定调用被观察者线程中的request(n) 从而 反向控制被观察者的发送事件速度

那么该什么时候调用被观察者线程中的request(n) & n 的值该是多少呢?
关于RxJava内部调用request(n)(n = 128、96、0)的逻辑如下:
点击查看

至于为什么是调用request(128) & request(96) & request(0),感兴趣的读者可自己阅读 Flowable的源码

用一个例子来演示该原理的逻辑

/ 被观察者:一共需要发送500个事件,但真正开始发送事件的前提 = FlowableEmitter.requested()返回值 ≠ 0
// 观察者:每次接收事件数量 = 48(点击按钮)

    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {

            Log.d(TAG, "观察者可接收事件数量 = " + emitter.requested());
                boolean flag; //设置标记位控制

                // 被观察者一共需要发送500个事件
                for (int i = 0; i < 500; i++) {
                    flag = false;

                    // 若requested() == 0则不发送
                    while (emitter.requested() == 0) {
                        if (!flag) {
                            Log.d(TAG, "不再发送");
                            flag = true;
                        }
                    }
                    // requested() ≠ 0 才发送
                    Log.d(TAG, "发送了事件" + i + ",观察者可接收事件数量 = " + emitter.requested());
                    emitter.onNext(i);


            }
        }
    }, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 设置被观察者在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, "接收到了事件" + integer);
                }

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

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


// 点击按钮才会接收事件 = 48 / 次
btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSubscription.request(48);
                // 点击按钮 则 接收48个事件
            }

        });

整个流程 & 测试结果: 请看图

5.3 采用背压策略模式:BackpressureStrategy

在Flowable的使用中,会被要求传入背压模式参数

在这里插入图片描述

  • 面向对象:针对缓存区
  • 作用:当缓存区大小存满、被观察者仍然继续发送下1个事件时,该如何处理的策略方式

缓存区大小存满、溢出 = 发送事件速度 > 接收事件速度 的结果 = 发送 & 接收事件不匹配的结果

5.3.1 背压模式类型

在这里插入图片描述

模式1:BackpressureStrategy.ERROR
  • 问题:发送事件速度 > 接收事件 速度,即流速不匹配

具体表现:出现当缓存区大小存满(默认缓存区大小 = 128)、被观察者仍然继续发送下1个事件时

  • 处理方式:直接抛出异常MissingBackpressureException

     Flowable.create(new FlowableOnSubscribe<Integer>() {
         @Override
         public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
    
             // 发送 129个事件
             for (int i = 0;i< 129; i++) {
                 Log.d(TAG, "发送了事件" + i);
                 emitter.onNext(i);
             }
             emitter.onComplete();
         }
     }, BackpressureStrategy.ERROR) // 设置背压模式 = BackpressureStrategy.ERROR
             .subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
             .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
             .subscribe(new Subscriber<Integer>() {
                 @Override
                 public void onSubscribe(Subscription s) {
                     Log.d(TAG, "onSubscribe");
                 }
    
                 @Override
                 public void onNext(Integer integer) {
                     Log.d(TAG, "接收到了事件" + integer);
                 }
    
                 @Override
                 public void onError(Throwable t) {
                     Log.w(TAG, "onError: ", t);
                 }
    
                 @Override
                 public void onComplete() {
                     Log.d(TAG, "onComplete");
                 }
             });
    

在这里插入图片描述

模式2:BackpressureStrategy.MISSING
  • 问题:发送事件速度 > 接收事件 速度,即流速不匹配

具体表现是:出现当缓存区大小存满(默认缓存区大小 = 128)、被观察者仍然继续发送下1个事件时

  • 处理方式:友好提示:缓存区满了

     Flowable.create(new FlowableOnSubscribe<Integer>() {
         @Override
         public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
    
             // 发送 129个事件
             for (int i = 0;i< 129; i++) {
                 Log.d(TAG, "发送了事件" + i);
                 emitter.onNext(i);
             }
             emitter.onComplete();
         }
     }, BackpressureStrategy.MISSING) // 设置背压模式 = BackpressureStrategy.MISSING
             .subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
             .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
             .subscribe(new Subscriber<Integer>() {
                 @Override
                 public void onSubscribe(Subscription s) {
                     Log.d(TAG, "onSubscribe");
                 }
    
                 @Override
                 public void onNext(Integer integer) {
                     Log.d(TAG, "接收到了事件" + integer);
                 }
    
                 @Override
                 public void onError(Throwable t) {
                     Log.w(TAG, "onError: ", t);
                 }
    
                 @Override
                 public void onComplete() {
                     Log.d(TAG, "onComplete");
                 }
             });
    

在这里插入图片描述

模式3:BackpressureStrategy.BUFFER

  • 问题:发送事件速度 > 接收事件 速度,即流速不匹配

具体表现是:出现当缓存区大小存满(默认缓存区大小 = 128)、被观察者仍然继续发送下1个事件时

  • 处理方式:将缓存区大小设置成无限大

1.即 被观察者可无限发送事件 观察者,但实际上是存放在缓存区
2.但要注意内存情况,防止出现OOM

    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {

            // 发送 129个事件
            for (int i = 1;i< 130; i++) {
                Log.d(TAG, "发送了事件" + i);
                emitter.onNext(i);
            }
            emitter.onComplete();
        }
    }, BackpressureStrategy.BUFFER) // 设置背压模式 = BackpressureStrategy.BUFFER
            .subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
            .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
            .subscribe(new Subscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    Log.d(TAG, "onSubscribe");
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d(TAG, "接收到了事件" + integer);
                }

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

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

可以接收超过原先缓存区大小(128)的事件数量了
在这里插入图片描述

模式4: BackpressureStrategy.DROP
  • 问题:发送事件速度 > 接收事件 速度,即流速不匹配

具体表现是:出现当缓存区大小存满(默认缓存区大小 = 128)、被观察者仍然继续发送下1个事件时

  • 处理方式:超过缓存区大小(128)的事件丢弃

如发送了150个事件,仅保存第1 - 第128个事件,第129 -第150事件将被丢弃

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                // 发送150个事件
                for (int i = 0;i< 150; i++) {
                    Log.d(TAG, "发送了事件" + i);
                    emitter.onNext(i);
                }
                emitter.onComplete();
            }
        }, BackpressureStrategy.DROP)      // 设置背压模式 = BackpressureStrategy.DROP
                .subscribeOn(Schedulers.io()) // 设置被观察者在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, "接收到了事件" + integer);
                }

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

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


btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSubscription.request(128);
                // 每次接收128个事件
            }

        });

被观察者一下子发送了150个事件,点击按钮接收时观察者接收了128个事件;再次点击接收时却无法接受事件,这说明超过缓存区大小的事件被丢弃了。

图片

模式5:BackpressureStrategy.LATEST
  • 问题:发送事件速度 > 接收事件 速度,即流速不匹配

具体表现是:出现当缓存区大小存满(默认缓存区大小 = 128)、被观察者仍然继续发送下1个事件时

  • 处理方式:只保存最新(最后)事件,超过缓存区大小(128)的事件丢弃

即如果发送了150个事件,缓存区里会保存129个事件(第1-第128 + 第150事件)

 Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                for (int i = 0;i< 150; i++) {
                    Log.d(TAG, "发送了事件" + i);
                    emitter.onNext(i);
                }
                emitter.onComplete();
            }
        }, BackpressureStrategy.LATEST) // // 设置背压模式 = BackpressureStrategy.LATEST
                 .subscribeOn(Schedulers.io()) // 设置被观察者在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, "接收到了事件" + integer);
                }

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

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

btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSubscription.request(128);
                // 每次接收128个事件
            }

        });
  • 被观察者一下子发送了150个事件,点击按钮接收时观察者接收了128个事件;
  • 再次点击接收时却接收到1个事件(第150个事件),这说明超过缓存区大小的事件仅保留最后的事件(第150个事件)

图片

背压策略模式小结

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RxJava 中,(backpressure)是一种处理数据流速不匹配的机制。当被观察者(Observable)发射数据速度快于观察者(Subscriber)处理数据的速度时,就会出现的情况。 为了处理RxJava 提供了一些操作符和策略,可以根据具体的需求选择适合的方法。下面是一些常用的方法: 1. `onBackpressureBuffer()`:将发射的数据缓存在一个无限大小的缓冲区中,直到观察者准备好处理数据。 ```java Observable.range(1, 1000) .onBackpressureBuffer() .observeOn(Schedulers.io()) .subscribe(new Subscriber<Integer>() { @Override public void onSubscribe(Subscription s) { s.request(Long.MAX_VALUE); } @Override public void onNext(Integer integer) { // 处理数据 } @Override public void onError(Throwable t) { // 处理错误 } @Override public void onComplete() { // 完成操作 } }); ``` 2. `onBackpressureDrop()`:当观察者无法及时处理数据时,丢弃多余的数据。 ```java Observable.range(1, 1000) .onBackpressureDrop() .observeOn(Schedulers.io()) .subscribe(new Subscriber<Integer>() { @Override public void onSubscribe(Subscription s) { s.request(Long.MAX_VALUE); } @Override public void onNext(Integer integer) { // 处理数据 } @Override public void onError(Throwable t) { // 处理错误 } @Override public void onComplete() { // 完成操作 } }); ``` 3. `onBackpressureLatest()`:只保留最新的数据,丢弃旧的数据。 ```java Observable.range(1, 1000) .onBackpressureLatest() .observeOn(Schedulers.io()) .subscribe(new Subscriber<Integer>() { @Override public void onSubscribe(Subscription s) { s.request(Long.MAX_VALUE); } @Override public void onNext(Integer integer) { // 处理数据 } @Override public void onError(Throwable t) { // 处理错误 } @Override public void onComplete() { // 完成操作 } }); ``` 这些方法可以在被观察者和观察者之间进行数据流速的调节,以避免问题。根据具体的业务需求,选择适合的处理方法即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值