RxJava Android toMap operator stops execution - android

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.

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());

RxJava operator Debounce is not working

I want to implement place autocomplete in Android application, and for this I'm using Retrofit and RxJava. I want to make response every 2 seconds after user type something. I'm trying to use debounce operator for this, but it's not working. It's giving me the result immediately without any pause.
mAutocompleteSearchApi.get(input, "(cities)", API_KEY)
.debounce(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(prediction -> Observable.fromIterable(prediction.getPredictions()))
.subscribe(prediction -> {
Log.e(TAG, "rxAutocomplete : " + prediction.getStructuredFormatting().getMainText());
});
As #BenP says in the comment, you appear to be applying debounce to the Place Autocomplete service. This call will return an Observable that emits a single result (or error) before completing, at which point the debounce operator will emit that one and only item.
What you probably want to be doing is debouncing the user input with something like:
// Subject holding the most recent user input
BehaviorSubject<String> userInputSubject = BehaviorSubject.create();
// Handler that is notified when the user changes input
public void onTextChanged(String text) {
userInputSubject.onNext(text);
}
// Subscription to monitor changes to user input, calling API at most every
// two seconds. (Remember to unsubscribe this subscription!)
userInputSubject
.debounce(2, TimeUnit.SECONDS)
.flatMap(input -> mAutocompleteSearchApi.get(input, "(cities)", API_KEY))
.flatMap(prediction -> Observable.fromIterable(prediction.getPredictions()))
.subscribe(prediction -> {
Log.e(TAG, "rxAutocomplete : " + prediction.getStructuredFormatting().getMainText());
});

RxJava filter 'else'

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.

Categories

Resources