RxJava2 / RxAndroidBle:从副作用订阅Observable

时间:2018-11-20 16:53:12

标签: rx-java2 rxandroidble

我有以下使用RxAndroidBle的简单BLE设备设置过程的用例:

  1. 连接到BLE设备。
  2. 开始侦听通知特征并设置解析器以解析每个传入的通知。然后,解析器将使用PublishSubject发布已解析的数据。
  3. 执行写到写特性(协商安全连接)。
  4. 等待解析器PublishSubject从设备-公钥(通过通知特征到达,作为对我们写操作的响应)传递已解析的响应。
  5. 对写入特性进行另一次写入(将连接设置为安全)。
  6. 提供Completable,说明该过程是否成功完成。

现在我的解决方案(不起作用)如下:

deviceService.connectToDevice(macAddress)
    .andThen(Completable.defer { deviceService.setupCharacteristicNotification() })
    .andThen(Completable.defer { deviceService.postNegotiateSecurity() })
    .andThen(Completable.defer {
        parser.notificationResultSubject
            .flatMapCompletable { result ->
                when (result) {
                    DevicePublicKeyReceived -> Completable.complete()
                    else -> Completable.error(Exception("Unexpected notification parse result: ${result::class}"))
                }
            }
    })
    .andThen(Completable.defer { deviceService.postSetSecurity() })

还有DeviceService类:

class DeviceService {

    /**
     * Observable keeping shared RxBleConnection for reuse by different calls
     */
    private var connectionObservable: Observable<RxBleConnection>? = null

    fun connectToDevice(macAddress: String): Completable {
        return Completable.fromAction {
            connectionObservable = 
                rxBleClient.getBleDevice(macAddress)
                .establishConnection(false) 
                .compose(ReplayingShare.instance())
        }
    }

   fun setupCharacteristicNotification(): Completable =
        connectionObservable?.let {
            it
                .switchMap { connection ->
                    connection.setupNotification(UUID_NOTIFICATION_CHARACTERISTIC)
                        .map { notificationObservable -> notificationObservable.doOnNext { bytes -> parser.parse(bytes) }.ignoreElements() }
                        .map { channel ->
                            Observable.merge(
                                Observable.never<RxBleConnection>().startWith(connection),
                                channel.toObservable()
                            )
                        }
                        .ignoreElements()
                        .toObservable<RxBleConnection>()
                }
                .doOnError { Timber.e(it, "setup characteristic") }
                .take(1).ignoreElements()
        } ?: Completable.error(CONNECTION_NOT_INITIALIZED)

   fun postNegotiateSecurity(): Completable {
        val postLength = negotiateSecurity.postNegotiateSecurityLength()
        val postPGK = negotiateSecurity.postNegotiateSecurityPGKData()

        return connectionObservable?.let {
            it.take(1)
                .flatMapCompletable { connection ->
                    postLength
                        .flatMapSingle { connection.write(it.bytes.toByteArray()) }
                        .doOnError { Timber.e(it, "post length") }
                        .flatMap {
                            postPGK
                                .flatMapSingle { connection.write(it.bytes.toByteArray()) }
                                .doOnError { Timber.e(it, "post PGK") }
                        }
                        .take(1).ignoreElements()
                }
        } ?: Completable.error(CONNECTION_NOT_INITIALIZED)
    }

    fun postSetSecurity(): Completable =
        connectionObservable?.let {
            it.take(1)
                .flatMapCompletable { connection ->
                    negotiateSecurity.postSetSecurity()
                        .flatMapSingle { connection.write(it.bytes.toByteArray()) }
                        .take(1).ignoreElements()
                }
        } ?: Completable.error(CONNECTION_NOT_INITIALIZED)
   }

private fun RxBleConnection.write(bytes: ByteArray): Single<ByteArray> =
    writeCharacteristic(UUID_WRITE_CHARACTERISTIC, bytes)

问题在于它被卡在deviceService.postNegotiateSecurity()中并且永远不会过去。我也没有在解析器中获得任何数据,所以我认为我在错误地订阅了通知特征。

negotiateSecurity.postNegotiateSecurityLength()negotiateSecurity.postNegotiateSecurityPGKData()是准备要发送的数据并将其作为Observable<SendFragment>传递的方法。由于数据帧大小的限制,一帧可能被编码为几个片段,然后由这些Observable发出。

1 个答案:

答案 0 :(得分:0)

回顾:

  • postNegotiateSecurity()从未完成
  • negotiateSecurity.postNegotiateSecurityLength()可能发射一次或多次
  • negotiateSecurity.postNegotiateSecurityPGKData()可能发射一次或多次

分析(为便于阅读,省略了日志):

it.take(1)
    .flatMapCompletable { connection ->
        postLength
            .flatMapSingle { connection.write(it.bytes.toByteArray()) }
            .flatMap {
                postPGK // may emit more than one value
                    .flatMapSingle { connection.write(it.bytes.toByteArray()) }
            }
            .take(1) // first emission from the above `flatMap` will finish the upstream
            .ignoreElements()
    }

postLength的每次发射都将开始特征写入。每次成功写入将开始订阅postPGK。如果postLength发出不止一次-将对postPGK进行更多订阅。

每次订阅postPGK极有可能导致多种排放。然后将每个发射平面映射到特征写入。每次成功写入都会发出一个值。

在上述特征写入的第一个发射之后,将处置上游(由于.take(1)运算符)。

如果postNegotiateSecurity()实际上已经启动,它也会完成或出错(假设postLengthpostPGK都发出至少一个值),因为这里没有其他逻辑。 / p>

结论

postNegotiateSecurity()很可能会完成(但不是以预期的方式),因为来自postPGK的第一个数据包会完成它。我假设外围设备在通知任何内容之前都希望有完整的数据,因此它正在等待PGK完全传输,而不会如上所示。

设置了RxBleLog.setLogLevel(RxBleLog.VERBOSE)的应用程序日志可以帮助您了解实际发生的情况。