在Android中合理的使用Rxjava封装同步和异步调用

同步和异步的区别:
 一般我们在程序中看到一些耗时的操作的方法,如果它有返回值等待调用者接收的情况就属于同步调用此时当前线程会
 阻塞在这里等待结果的返回
 异步调用方法一般会调用方法是传入响应的监听器类listener通过接口回调的方式来通知调用者结果而当前线程是不
 会阻塞等待结果返回的

 当需要封装现有 API 为 Observable 的时候,可以从一下几点来考虑:
   如果为同步 API 则使用 Observable.fromCallable()
   如果为异步 API 则:
      避免使用 Observable.create()
      使用 Flowable.create() 并正确实现如下步骤
      在合适的地方调用 onNext(), onCompleted(), 和 onError()
      如果需要清理资源,则使用 setCancellation()
      选择正确的 BackPressure 背压策略
1.rxjava封装同步调用 比如使用rxjava来封装SharePreference的使用
 RxView.clicks(findViewById(R.id.btn_rxjava_wrap_sync))
                .compose(RxUtils.preventDuplicateClicks())
                .subscribe(new Consumer() {
                    @Override
                    public void accept(Object o) throws Exception {

                        rxWrapSyncForSharepreferences(sharedPreferences).subscribe(new Consumer() {
                            @Override
                            public void accept(Boolean aBoolean) throws Exception {
                                Toast.makeText(RxjavaWrapAsyncAndsyncActivity.this, aBoolean ? "成功" : "失败", Toast.LENGTH_SHORT).show();
                            }
                        }, new Consumer() {
                            @Override
                            public void accept(Throwable throwable) throws Exception {
                                throwable.printStackTrace();
                                Log.d(TAG, "accept: " + throwable.getMessage());
                            }
                        });
                    }
                });
 private Observable rxWrapSyncForSharepreferences(final SharedPreferences sharedPreferences) {
        return Observable.fromCallable(new Callable() {
            @Override
            public Boolean call() throws Exception {
                boolean result = sharedPreferences.edit().putInt("a", 1)
                        .putInt("b", 2)
                        .putString("c", "c")
                        .commit();

                Log.d(TAG, "call: thread : " + Thread.currentThread().getName());

                return result;
            }
        }).compose(RxUtils.observableToMain());
    }

以上的场景为当我们点击按钮时会触发sp存储一些数据的操作 并且我们需要得到sp是否存储成功的返回值因此此时我们就可以使用Rxjava来封装这个操作

 首先我们使用Observable.fromCallable(new Callable(){...})的方式包装我们的sp存储操作同时使用

RxUtils.observableToMain()即

public static  ObservableTransformer observableToMain(){
        return new ObservableTransformer() {
            @Override
            public ObservableSource apply(@NonNull Observable upstream) {
                return upstream.subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }

将我们的sp存储操作放到子线程进行操作 并同时以返回Observable的形式给下游进行存储结果的订阅

2.使用Rxjava封装异步操作

 而在 Android 中还有很多异步回调场景,在网络上到处可以看到使用 Observable.create() 来封装异步调用的示例

但是,使用 Observable.create() 有很多缺点.。

下面使用一个示例来看看如何封装异步 API。假设我们用 SensorManager 来追踪设备的加速度。 普通的实现方式是这样的:

public class RxjavaWrapAsyncAndsyncActivity extends AppCompatActivity {


    private SensorManager sensonManager;
    private Sensor sensor;

    private Disposable disposable1;
    private Disposable disposable2;
    private Disposable netWorkDisposable;

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

        sensonManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        sensor = sensonManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
   }


    SensorEventListener listener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            Log.d(TAG, "onSensorChanged: " + Arrays.toString(event.values));
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
            Log.d(TAG, "onAccuracyChanged: " + accuracy);
        }
    };

    @Override
    protected void onResume() {
        super.onResume();
       sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_NORMAL); 
    }

    @Override
    protected void onPause() {
        super.onPause();

        sensonManager.unregisterListener(listener, sensor);
    }
}

而下面是一种使用Observable.create()的方式封装为 RxJava Observable 的方式:

public Observable getSensorDataAsync1(final SensorManager sensonManager, final Sensor sensor, final int timespan) {


        return Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(@NonNull final ObservableEmitter emitter) throws Exception {

                if(emitter.isDisposed()){
                    return;
                }

                final SensorEventListener sensorEventListener = new SensorEventListener() {
                    @Override
                    public void onSensorChanged(SensorEvent event) {
                        Log.d(TAG, "onSensorChanged: observable thread : " + Thread.currentThread().getName());

                        if (!emitter.isDisposed()) {
                            //由于加速度传感器会一直发射数据因此在此处
                           //我们需要的是一个热的Hot Observable 因此此处
                            //没有调用onCompleted()方法
                            emitter.onNext(event);
                        }
                    }

                    @Override
                    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
                };

             sensonManager.registerListener(sensorEventListener, sensor, timespan);

            }
        }).compose(RxUtils.observableToMain());
    }

以上这种使用Observable.create()封装异步操作的方式有很大的缺陷:

虽然上面的代码可以工作。但是距离一个符合 Observable 规范的实现来差很多, Observable 规范有如下几点:
1. 如果 Subscriber 取消注册了,则需要取消注册加速度的监听器来避免内存泄露
2. 需要捕获可能出现的异常,然后把异常传递到 Observable 的 onError() 函数来避免导致程序崩溃
3. 在调用 onNext() 或者 onError() 之前,需要判断 Subscriber 是否还在监听事件,避免发射不必要的数据
4. 需要处理 backpressure(数据发射的速度比 Subscriber 处理的速度要快的情况),防止 MissingBackpressureException 异常。
上面的4点中使用Observable.create()还是可以实现前面四点的

public Observable getSensorDataAsync1(final SensorManager sensonManager, final Sensor sensor, final int timespan) {


        return Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(@NonNull final ObservableEmitter emitter) throws Exception {

                if(emitter.isDisposed()){
                    return;
                }

                final SensorEventListener sensorEventListener = new SensorEventListener() {
                    @Override
                    public void onSensorChanged(SensorEvent event) {
                        Log.d(TAG, "onSensorChanged: observable thread : " + Thread.currentThread().getName());

                        if (!emitter.isDisposed()) {
                            emitter.onNext(event);
                        }
                    }

                    @Override
                    public void onAccuracyChanged(Sensor sensor, int accuracy) {

                    }
                };


                //这个地方使用setCancellable()或者setDisposable()的区别
//                https://github.com/ReactiveX/RxJava/issues/4812
//                https://stackoverflow.com/questions/43280248/what-is-the-difference-between-rxjava-2-cancellable-and-disposable#
                emitter.setCancellable(new Cancellable() {
                    @Override
                    public void cancel() throws Exception {
//                        throw new Exception("异常终止");
                        sensonManager.unregisterListener(sensorEventListener, sensor);
                    }
                });

                sensonManager.registerListener(sensorEventListener, sensor, timespan);

            }
        }).compose(RxUtils.observableToMain());
    }

但是要实现 上面的第4步就没有这么简单了,毕竟 RxJava 中的 Backpressure 都可以出一本书来详细介绍其内容了。

每次封装异步 API 都需要手工实现 Backpressure 则是非常痛苦的,也不是常人可以正确做到的。

因此,在 RxJava 2.x版本中,RxJava 开发人员提供了一个新的类型的Observable即Flowable

Flowable和Observable的唯一区别就是Flowable默认是支持背压backpressure的因此我们可使用Flowable来实现我们的背压策略

private Flowable getSensorDataAsync2(final SensorManager sensonManager, final Sensor sensor, final int timespan) {
        return Flowable.create(new FlowableOnSubscribe() {
            @Override
            public void subscribe(@NonNull final FlowableEmitter emitter) throws Exception {
                Log.d(TAG, "flowable thread " + Thread.currentThread().getName());

                if(emitter.isCancelled()){
                    return;
                }

                final SensorEventListener sensorEventListener = new SensorEventListener() {
                    @Override
                    public void onSensorChanged(SensorEvent event) {
                        Log.d(TAG, "onSensorChanged: flowable thread " + Thread.currentThread().getName());
                        if (!emitter.isCancelled()) {
                            emitter.onNext(event);
                            //此处Flowable是一个热的Flowable
                            //此处不能调用onComplete()我们是希望订阅者一直接受到sensor的数据
//                            emitter.onComplete();
                        }
                    }

                    @Override
                    public void onAccuracyChanged(Sensor sensor, int accuracy) {

                    }
                };


                emitter.setCancellable(new Cancellable() {
                    @Override
                    public void cancel() throws Exception {
                        sensonManager.unregisterListener(sensorEventListener, sensor);
                    }
                });


                sensonManager.registerListener(sensorEventListener, sensor, timespan);

            }
        }, BackpressureStrategy.BUFFER) //指定一种背压策略
                .compose(RxUtils.flowableToMain());
    }

注意上面的实现中,并没有处理 第 2、3 步骤,Flowable.create() 自动处理了该问题。
通过 setCancellation() 来实现 第 1 个步骤,而第 4 个步骤只要指定 backpressure 背压策略就可以了

处理 Backpressure

Backpressure 是用来描述,生产者生产数据的速度比消费者消费数据的速度快的一种情况。

如果没有处理这种情况,则会出现 MissingBackpressureException 。

由于手工实现 Backpressure 是很困难的,如果使用 fromAsync() 函数则我们只需要理解各种

Backpressure 策略即可,不用自己实现。

BackpressureMode 有如下几种策略:

  • BUFFER(缓存):使用无限个数的内部缓存(RxJava 默认使用 16 个元素的内部缓存),一开始会创建一个 128 个元素的缓冲对象,然后动态的扩展直到 JVM 内存不足。

    使用 hot Observable 的时候通常会指定这种策略,比如上面的示例。

  • LATEST(使用最新的):只发射最新生成的数据,之前的旧的数据被丢弃。类似于使用缓冲个数为 1 的缓存。

    cold Observable 通常可以使用这种策略。比如 Andorid 里面的电量变化、或者 最近的位置信息就可以使用这种策略。之前旧的数据已经为无效数据直接丢弃就好。

  • DROP(直接丢弃):只发射第一个数据,之后的所有数据就丢弃。

    通常用来指生成一个数据的 Observable。

  • ERROR / NONE: 默认的不指定任何策略,会出现 MissingBackpressureException

     

在封装异步 API 的时候,根据异步 API 的特点,来选择合适的策略是非常重要

你可能感兴趣的:(Rxjava)