RxJava filter 'else' - android

I want to split my observable like if/else statement
Something like:
A[] array = new A[10];
for (int i = 0; i < array.length; i++) {
array[i] = new A(getRandomString(), getRandomInt());
}
Observable.from(array)
.filter(a -> condition(a))
.// <--- do stuff if condition returns true
.// <- back to parent
.filter(a -> complexCondition(a)) // filter all elements(!)
.// <--- do stuff if complex condition returns true
.// <- back to iterate all elements
Is it even possible?

In Observable does not work in that way, you have to think that the observable is just like a stream, where you can end up with some items, u others.
The most close if/else that you will find in observables will be use GroupsBy.
Here you have some practicle example that I did explaining how works
https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/transforming/ObservableGroupBy.java

One way to achieve this behaviour is to subscribe to your data twice and filter it differently:
Subscription subscriptionA = Observable.from(array)
.filter(a -> condition(a))
.subscribe(...) // <-- do stuff for condition A
Subscription subscriptionB = Observable.from(array)
.filter(a -> complexCondition(a))
.subscribe(...) // <-- do stuff for condition B

To expand on Ken's answer, another option is to "fork" the Observable into the two branches, using the replay operator. This guarantees that the original observable is only called once. This is useful if there's some expensive processing or side effects somewhere in the chain:
ConnectableObservable<A> connectable = Observable.fromArray()
.replay();
connectable
.filter(a -> condition(a))
.// <--- do stuff if condition returns true
connectable.connect(); // do this after the first branch
connectable
.filter(a -> complexCondition(a)) // filter all elements(!)
.// <--- do stuff if complex condition returns true
connectable
.// <- iterate all elements
Remember that all branches must handle both the onNext and the onError events.

Related

How to enable/disable to notification/indication in RxAndroidBLE

I am creating a RxJava2 chain where in I want to enable and disable notification. the flow I am setting is as follows.
establish a connection.
set the notification to READ_STATUS UUID.
if the returned byte is zero then perform a write byte 01 to WRITE_STATUS UUID and after WRITE_STATUS, enable the notification of READ_STATUS UUID to verify it has byte value 1.
else if the returned byte is 1 then just enable other indicators (UUID1, UUID2,UUD3) and read the value.
I have a problem at step 2 and 3 where I am reading the value of READ_STATUS UUID by enabling the notification. in order to re-read the value, I probably need to disable the notification and then again enable it. And to disable to the notification I have to dispose that particular setupNotification .
Code is as follows
connectDisposable=
device.establishConnection(false)
.flatMap(rxBleConnection -> {
rxBleConnection.discoverServices();
mRxBleConnection = rxBleConnection;
return Observable.just(rxBleConnection);
})
.flatMap(rxBleConnection ->mRxBleConnection.setupNotification(READ_STATUS,NotificationSetupMode.QUICK_SETUP).flatMap(it->it))
.takeUntil(bytes -> {
if(getByteValue(bytes)==0)
return false;// dispose above to disable the notification
else
return true; // no need to disable the notification and continue writing
})
.flatMap(bytes -> {
return Observable.zip(
mRxBleConnection.writeCharacteristic(WRITE_STATUS, new byte[]{1}).toObservable(),
// setupNotification again to check whether read status has 1 or not
mRxBleConnection.setupNotification(READ_STATUS, NotificationSetupMode.QUICK_SETUP).flatMap(it->it),
Pair::new
);
})
.flatMap(bytes ->{
byte [] val= bytes.first;
if(getByteValue(val) == 1){
return Observable.zip(
mRxBleConnection.setupIndication(HISTORY, NotificationSetupMode.QUICK_SETUP).doOnNext(observable -> Log.e(TAG,"Here 1 ")).flatMap(it -> it),
mRxBleConnection.setupIndication(PARAMCHECK, NotificationSetupMode.QUICK_SETUP).doOnNext(observable -> Log.e(TAG,"Here 2 ")).flatMap(it -> it),
mRxBleConnection.setupIndication(FAULTINFO, NotificationSetupMode.QUICK_SETUP).doOnNext(observable -> Log.e(TAG,"Here 3 ")).flatMap(it -> it),
Data::Readings);
}
return Observable.empty();
}).subscribe(data -> {
});
The problem with this code is my takeUntil is firing at the last it does not dispose the previous setupNotificaion operation so that I can re read it later.
I tried solution mentioned over this thread but unfortunately I am not sharing the RxBleConnection
The problem with this code is my takeUntil is firing at the last it does not dispose the previous setupNotificaion operation so that I can re read it later.
The problem is that your condition is inverted. From .takeUntil() Javadoc:
* #return an Observable that first emits items emitted by the source Observable, checks the specified
* condition after each item, and then completes when the condition is satisfied.
You have used:
.takeUntil(bytes -> {
if(getByteValue(bytes)==0)
return false;// dispose above to disable the notification
else
return true; // no need to disable the notification and continue writing
})
where it should be satisfied (return true) when the upstream should get disposed:
.takeUntil(bytes -> {
if(getByteValue(bytes)==0)
return true;// dispose above to disable the notification
else
return false; // no need to disable the notification and continue writing
})
To unsubscribe or to dispose setupNotification or setupIndication one can use the following code. I am sure there could be different ways but so far I could find this
private Observable<Pair<byte[],byte[]>> getValueFromIndication(RxBleConnection rxBleConnection){
final PublishSubject<Boolean> unsubscribeOperation= PublishSubject.create();
return Observable.zip(
rxBleConnection.setupIndication(TSDictionary.FAULT_RETRY_COUNT_SEQUENCE,NotificationSetupMode.QUICK_SETUP).flatMap(it->it).takeUntil(unsubscribeOperation),
rxBleConnection.setupIndication(TSDictionary.FAULT_RETRY_INFORMATION,NotificationSetupMode.QUICK_SETUP).flatMap(it->it).takeUntil(unsubscribeOperation),
(bytes, bytes2) -> {
unsubscribeOperation.onNext(true);
return Pair.create(bytes,bytes2);
}
);
}
In above code, I am zipping two indication operations and once I get the value from it I am unsubscribing from the change chain using PublishSubject and takeUntil.

How to make a set of parallel of undefined number of requests using rxJava in Android?

Currently I have a service that returns that returns a list of parameters. if there are 4 parameters I need to perform one request per parameter to the same endpoint using the each of the parameter. After that I need to save the list of results of all the request into a collection. If I don't know how many request do I have to perform, What rxJava operator I need to use and how should I use it?? .
Take into account that I don't need to wait for the answer of the first request to perform the second one and ....
I have seen that the zip operator allow me to perform parallel request but I have to know the number of request to use it.
You can use flatMap to create Observable for each parameter and execute them in parallel as in
Observable.fromArray(parameters)
.flatMap(val -> Observable.just(val)
.subscribeOn(Schedulers.io())
.map(request -> doApiCall(request))
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> log(response));
At the end I implemented it this way:
public Subscription getElementsByStage(List <String> requiredStages) {
List < Observable <ElementsResponse>> observables = new ArrayList < > ();
for (String stage: requiredStages) {
ElementsRequest request = buildElementRequest(stage);
observables.add(request).subscribeOn(Schedulers.newThread()));
}
Observable zippedObservables = Observable.zip(observables, this::arrangeElementsByStage);
return zippedObservables
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber <HashMap<Stage,Element>>() {
.....
}
}

RxJava2 materialize() doesn't emit next item after an error

I've been trying to convert my onErrors into notifications in order to keep the stream emitting items. As far as I understood the materialize() operator does just that. So basically:
materialize() / dematerialize() are available to turn terminal events
into Notification
So I made a test for this based on this question (How to continue streaming items after error in RxJava?). I tried the following:
#Test
public void materializeTest() {
final Observable<String> stringObservable = Observable.fromArray("1", "2", "3")
.flatMap(x -> {
if (x.equals("2")) {
return Observable.error(new NullPointerException());
}
return Observable.just(x);
})
.materialize()
.map(n -> n.getValue());
final TestObserver<String> testObs = stringObservable.test();
Java6Assertions.assertThat(testObs.values().size()).isEqualTo(2);
testObs.assertValueAt(0, "1");
testObs.assertValueAt(1, "3");
}
The result is that no more items are emitted after "2" gives the error. I've also tried to warp on my own Notification object (MyNotification<T>) and do something like:
stringObs
.map(string -> MyNotification.success(string)
.onErrorReturn(error -> MyNotification.error())
But the end result is always the same: after "2" no more items are emitted. I'm 100% doing something wrong but can't really understand what is.
With flatMap, if one of the inner Observables fails, the sequence is terminated an no further items are transformed from the upstream. That happens before materialize() even gets involved.
So instead of trying to materialize the merged flow, materialize the inner sources individually:
Observable.fromArray("1", "2", "3")
.flatMap(x -> {
if (x.equals("2")) {
return Observable.<String>error(new NullPointerException())
.materialize();
}
return Observable.just(x)
.materialize();
})
.filter(n -> n.isOnNext())
.map(n -> n.getValue());

Determine Characteristic Notfication emission count by first emission

I am currently implementing a protocol for a Bluetooth device and i am using the RxAndroidBle Library (version 1.4.3).
I have to request data from the device by writing to characteristic and then listening to the response via a characteristic notification.
To combine the 2 operations (writing and listening) I am using the code from: https://stackoverflow.com/a/41140523/734385
connectionObservable
.flatMap( // when the connection is available...
rxBleConnection -> rxBleConnection.setupNotification(AP_SCAN_DATA), // ... setup the notification...
(rxBleConnection, apScanDataNotificationObservable) -> Observable.combineLatest( // ... when the notification is setup...
rxBleConnection.writeCharacteristic(AP_SCAN_DATA, writeValue), // ... write the characteristic...
apScanDataNotificationObservable.first(), // ... and observe for the first notification on the AP_SCAN_DATA
(writtenBytes, responseBytes) -> responseBytes // ... when both will appear return just the response bytes...
)
)
.flatMap(observable -> observable)
This approach works for me, the only problem is that the code gives me only the first 20 bytes (due to the apScanDataNotificationObservable.first()).
Unfortunately, I don't know the size of the package I am receiving. I can only extract the information from the header of the first 20 bytes. It seems like the RxJava buffer function all require to know the size beforehand.
Is there a way to make this work cleanly with the code above as part of the Rx chain?
In other words, can I control the number of emission based on the very first emission of an Rx chain?
Or do I have a completely wrong approach?
It is possible to achieve what you want.
The easiest way would be to exchange the Observable.combineLatest(...) to:
Observable.merge(
rxBleConnection.writeCharacteristic(AP_SCAN_DATA, writeValue).ignoreElements(), // send the request but ignore the returned value
apScanDataNotificationObservable.takeUntil(newResponseEndWatcher()) // take the response notifications until the response end watcher says so
);
Where newResponseEndWatcher() would need to contain the logic for determining if the received values are all that is expected. It could look like this:
private Func1<byte[], Boolean> newResponseEndWatcher() {
return new Func1<byte[], Boolean>() {
private static final int NOT_INITIALIZED = -1;
private int totalLength = NOT_INITIALIZED;
private int receivedLength = NOT_INITIALIZED;
#Override
public Boolean call(byte[] bytes) {
if (isNotInitialized(totalLength)) { // if it is the first received value
// parse totalLength from the header
}
// update receivedLength
return receivedLength >= totalLength;
}
private boolean isNotInitialized(int value) {
return value == NOT_INITIALIZED;
}
};
}
Just have in mind that Func1 which is the result newResponseEndWatcher() is stateful. If one would store into a variable the observable that is result of apScanDataNotificationObservable.takeUntil(newResponseEndWatcher()) the next subscriptions could end prematurely.
To mitigate this problem one may use Observable.using() function that would call newResponseEndWatcher() each time it is subscribed and then create a new apScanDataNotificationObservable.takeUntil(newResponseEndWatcher):
Observable.using(
() -> newResponseEndWatcher(), // create a new response end watcher on each subscription
responseEndWatcher -> apScanDataNotificationObservable.takeUntil(responseEndWatcher), // create the response observable that will complete properly
responseEndWatcher -> { /* ignored, responseEndWatcher will get GCed eventually */ }
);

RxJava Android toMap operator stops execution

Hello I tried use rx java for next task
Show fields of form
Show errors on mistaked fields
Return Observable> of valid field -> value
Solution
List<Observable<RxUtil.EditTextEvent>> listOfObs = new ArrayList<>();
...fill form, fill listOfObs
return Observable.merge(listOfObs) // stream of EdtText events
.debounce(5, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext((onTextChangeEvent) -> { // show error, if user made mistake
if (!CheckOutUtils.isInputCorrect(onTextChangeEvent))
onTextChangeEvent.editText.setError("Проверьте поле");
})
.filter(CheckOutUtils::isInputCorrect) // only valid fields
.toMap(editTextEvent -> (Field) editTextEvent.editText.getTag(), editTextEvent -> editTextEvent.text) // called on every change
.doOnNext(fieldStringMap -> {
Log.v("map", fieldStringMap.toString()); // never called
}); // map of it
The last operator is never called. toMap is last executing operator. What's wrong?
I'd listen to the text change events and do a sweep over all text fields in some way:
List<TextField> fields = ...
Observable<TextField> all = Observable.from(fields);
all
.flatMap(f -> RxUtil.textChangedObservable(f))
.debounce(5, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(f ->
all
.filter(f -> isCorrect(f.getText()))
.toMap(f -> f.getTag(), f -> getText())
)
.subscribe(System.out::println);
I'm not familiar with RxUtil but you'll probably find the necessary methods.
The problem is most likely that your source observables do not all complete. The toMap() operator will not call onNext() until the source observable completes. Here's the marble diagram for toMap() for reference.

Categories

Resources