我应该将RxJava订阅链中的onBackPressureBuffer(n)放在哪里?

时间:2019-03-16 17:19:18

标签: react-native rx-java react-native-ble-plx

我正在尝试修复现有的React Native库react-native-ble-plx,并在existing Java code中添加onBackPressureBuffer()。

我知道这很丑陋,但是我现在没有时间提交PR,并且有一个pending issue可以解决问题。 我这样做是因为事件发射器的工作频率为200Hz。我需要一种安全的方法来在本机端缓冲项目,同时在JavaScript端按自己的速度消费它们。

所以代码如下:

       final Subscription subscription = Observable.defer(new Func0<Observable<Observable<byte[]>>>() {
            @Override
            public Observable<Observable<byte[]>> call() {
                int properties = gattCharacteristic.getProperties();
                BluetoothGattDescriptor cccDescriptor = gattCharacteristic
                        .getDescriptor(Characteristic.CLIENT_CHARACTERISTIC_CONFIG_UUID);
                NotificationSetupMode setupMode = cccDescriptor != null ? NotificationSetupMode.QUICK_SETUP
                        : NotificationSetupMode.COMPAT;
                if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                    return connection.setupNotification(gattCharacteristic, setupMode);
                }

                if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
                    return connection.setupIndication(gattCharacteristic, setupMode);
                }

                return Observable.error(new CannotMonitorCharacteristicException(gattCharacteristic));
            }
        }).onBackpressureBuffer(1000)  <---- Here is my modification
.flatMap(new Func1<Observable<byte[]>, Observable<byte[]>>() {
            @Override
            public Observable<byte[]> call(Observable<byte[]> observable) {
                return observable;
            }
        }).doOnUnsubscribe(new Action0() {
            @Override
            public void call() {
                promise.resolve(null);
                transactions.removeSubscription(transactionId);
            }
        }).subscribe(new Observer<byte[]>() {
            @Override
            public void onCompleted() {
                promise.resolve(null);
                transactions.removeSubscription(transactionId);
            }

            @Override
            public void onError(Throwable e) {
                errorConverter.toError(e).reject(promise);
                transactions.removeSubscription(transactionId);
            }

            @Override
            public void onNext(byte[] bytes) {
                characteristic.logValue("Notification from", bytes);
                WritableArray jsResult = Arguments.createArray();
                jsResult.pushNull();
                jsResult.pushMap(characteristic.toJSObject(bytes));
                jsResult.pushString(transactionId);
                sendEvent(Event.ReadEvent, jsResult);
            }
        });

我的问题是,即使添加了这些东西,我仍然遇到MissingBackPressure异常。

我尝试过onBackPressureDrop(),但行为却完全相同。因此,我认为我做错了,但是现在不知道为什么。

任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

正如您所说的,您在使用react-native库时遇到了问题,上面的代码以前确实抛出过MissingBackpressureException

.onBackpressureDrop()的Javadoc中摘录:

  

指示一个Observable发出的项目快于其观察者消耗它们丢弃的速度,   而不是发出观察者不准备观察的那些项目。   

     

  如果下游请求计数为0,则Observable将避免调用{@code onNext},直到   观察者再次调用{@code request(n)}来增加请求数。

     
   
背压:
   
操作员尊重下游的背压,并以无限制的方式消耗{@code Observable}源    方式(即不对其施加反压)。
   
计划程序:
   
{@ code onBackpressureDrop}在特定的{@link Scheduler}上默认不运行。
  

您会看到链中的下一个运算符是.flatMap().doOnUnsubscribe().subscribe()

来自.flatMap()的Javadoc关于反压:

  

背压:
  
操作员尊重下游的背压。外部{@code Observable}被消耗了   在无限制模式下(即未对其施加反压)。内部{@code Observable}有望兑现   背压;如果违反,则操作员可能发出{@code MissingBackpressureException}信号。

Javadoc .doOnUnsubscribe()

  

背压:
  
{@ code doOnUnsubscribe}不会与反压力请求或值传递交互;背压   行为保留在上游和下游之间。

还有.subscribe()

  

背压:
  
操作员以无限制的方式使用了{@code Observable}源(即,否   施加反压)。

如您所见,.onBackpressure*()下面的运算符都没有对其施加反压。您需要添加一个在.onBackpressure*()之后执行此操作的运算符。 .observeOn(Scheduler)

是此类运算符之一

Javadoc .observeOn()

  

      背压:    该操作员尊重来自下游的背压,并期望来自源{@code Observable}的背压。违反此    期望将导致{@code MissingBackpressureException}。这是最常见的运算子,    弹出;在供应链中寻找不支持背压的资源,    例如{@code interval},{@ code timer},{code PublishSubject}或{@code BehaviorSubject},然后应用任何    {strong>之前操作者中的{@code onBackpressureXXX}操作者应用{@code watchOn}本身。

因此可行的代码如下:

final Subscription subscription = Observable.defer(new Func0<Observable<Observable<byte[]>>>() {
    @Override
    public Observable<Observable<byte[]>> call() {
        int properties = gattCharacteristic.getProperties();
        BluetoothGattDescriptor cccDescriptor = gattCharacteristic
                .getDescriptor(Characteristic.CLIENT_CHARACTERISTIC_CONFIG_UUID);
        NotificationSetupMode setupMode = cccDescriptor != null ? NotificationSetupMode.QUICK_SETUP
                : NotificationSetupMode.COMPAT;
        if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
            return connection.setupNotification(gattCharacteristic, setupMode);
        }

        if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
            return connection.setupIndication(gattCharacteristic, setupMode);
        }

        return Observable.error(new CannotMonitorCharacteristicException(gattCharacteristic));
    }
})
.flatMap(new Func1<Observable<byte[]>, Observable<byte[]>>() {
    @Override
    public Observable<byte[]> call(Observable<byte[]> observable) {
        return observable;
    }
})
.doOnUnsubscribe(new Action0() {
    @Override
    public void call() {
        promise.resolve(null);
        transactions.removeSubscription(transactionId);
    }
})
.onBackpressureBuffer(1000) // <---- Here is my modification
.observeOn(Schedulers.trampoline()) // <---- an operator that does backpressure the above
.subscribe(new Observer<byte[]>() {
    @Override
    public void onCompleted() {
        promise.resolve(null);
        transactions.removeSubscription(transactionId);
    }

    @Override
    public void onError(Throwable e) {
        errorConverter.toError(e).reject(promise);
        transactions.removeSubscription(transactionId);
    }

    @Override
    public void onNext(byte[] bytes) {
        characteristic.logValue("Notification from", bytes);
        WritableArray jsResult = Arguments.createArray();
        jsResult.pushNull();
        jsResult.pushMap(characteristic.toJSObject(bytes));
        jsResult.pushString(transactionId);
        sendEvent(Event.ReadEvent, jsResult);
    }
});