How to concatenate two observable operations in a linear fashion (do first this thing and after that one finishes do the second thing)?

╄→гoц情女王★ 提交于 2020-02-03 08:27:08

问题


Polidea has released a new handy library called RxAndroidBle which is very useful for dealing with a lot of issues when you're using vanilla Bluetooth APIs.

Before explaining more the idea is to have a POJO model that has all the recent values that the device send (or I query) to me (in this case is represented by a Map object):

If I want to be notified about multiple characteristic notifications I can do this:

final UUID serviceUuid = // your service UUID
final Map<UUID, byte[]> genericModel = new HashMap<>();
final Observable<RxBleConnection> connectionObservable = // your connectionObservable

connectionObservable
        .flatMap(connection -> 
                connection.discoverServices()
                        .flatMap(services -> services.getService(serviceUuid).map(BluetoothGattService::getCharacteristics)) // get characteristics you're interested in
                        .flatMap(Observable::from) // deal with every characteristic separately
                        .flatMap(characteristic -> connection
                                        .setupNotification(characteristic) // setup notification for each
                                        .flatMap(observable -> observable), // to get the raw bytes from notification
                                Pair::new) // merge characteristic with byte[] to keep track from which characteristic the bytes came
        )
        .subscribe(
                pair -> genericModel.put(pair.first.getUuid(), pair.second),
                throwable -> { /* handle errors */}
        );

And now when I'm connected to the device the notifications are updating the POJO object (the Map in this particular example).

If I want to read values I can do the following:

 connectionObservable.flatMap(connection ->
    connection.discoverServices()
        .flatMap(services -> services.getService(SERVICE_ID).map(BluetoothGattService::getCharacteristics))
        .flatMap(Observable::from)
        .filter(characteristic -> BleUtils.hasReadProperty(characteristic.getProperties()))
        .flatMap(connection::readCharacteristic, Pair::new)
)
    .subscribe(
            pair -> genericModel.put(pair.first.getUuid(), pair.second),
            throwable -> { /* handle errors */}
    );

My main question is:

I would like to upon the first connection: read an amount of characteristics that has the reading property and only after that subscribe to those notifications that have the notification property. This is concatenate the operations. How I can achieve this?

The thing is when I do the first part of the reading, the observable read those characteristics properties but won't emit a doOnComplete method as is waiting for more so I can't kickstart or compose the next operation which is subscribing and listening to changes.

I know for sure the amount of characteristic that has the read property but I would like to do it in a generic fashion (i.e it doesn't matter if I have 7 or 15 characteristics that I want read, I only want to read them all, write the pojo values, and after that start listening the notifications).

Maybe the option is to compose an observable that counts the successful reading and after that it starts listening notifications.

How is the best reactive approach to achieve that?

To put you in situation this is the original thread that spawned this question.


回答1:


To achieve what you want you can take several ways. One of them is:

    final UUID serviceUuid = // your service UUID
    final Map<UUID, byte[]> genericModel = new HashMap<>();
    final Observable<RxBleConnection> connectionObservable = // your connectionObservable

    connectionObservable
            .flatMap( // get the characteristics from the service you're interested in
                    connection -> connection
                            .discoverServices()
                            .flatMap(services -> services
                                    .getService(serviceUuid)
                                    .map(BluetoothGattService::getCharacteristics)
                            ),
                    Pair::new
            )
            .flatMap(connectionAndCharacteristics -> {
                final RxBleConnection connection = connectionAndCharacteristics.first;
                final List<BluetoothGattCharacteristic> characteristics = connectionAndCharacteristics.second;
                return readInitialValues(connection, characteristics)
                        .concatWith(setupNotifications(connection, characteristics));
            })
            .subscribe(
                    pair -> genericModel.put(pair.first.getUuid(), pair.second),
                    throwable -> { /* handle errors */}
            );

Where:

private Observable<Pair<BluetoothGattCharacteristic, byte[]>> readInitialValues(RxBleConnection connection,
                                                                                List<BluetoothGattCharacteristic> characteristics) {
    return Observable.from(characteristics) // deal with every characteristic separately
            .filter(characteristic -> (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0) // filter characteristics that have read property
            .flatMap(connection::readCharacteristic, // read characteristic
                    Pair::new); // merge characteristic with byte[] to keep track from which characteristic the bytes came
}

private Observable<Pair<BluetoothGattCharacteristic, byte[]>> setupNotifications(RxBleConnection connection,
                                                                                List<BluetoothGattCharacteristic> characteristics) {
    return Observable.from(characteristics) // deal with every characteristic separately
            .filter(characteristic -> (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) // filter characteristics that have notify property
            .flatMap(characteristic -> connection
                            .setupNotification(characteristic) // setup notification for each
                            .flatMap(observable -> observable), // to get the raw bytes from notification
                    Pair::new); // merge characteristic with byte[] to keep track from which characteristic the bytes came
}

Alternatively to concatWith() you could inverse the order and use startWith().

Best Regards



来源:https://stackoverflow.com/questions/39140906/how-to-concatenate-two-observable-operations-in-a-linear-fashion-do-first-this

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!