Rx SearchView needs to cancel on going request with less priority - android

I am using
RxSearchView.queryTextChangeEvents
to detect the events of “Search As You Type” and also when you submit a search,

SAYT is to get suggestions and when you do a submit, it executes a full search.
There are 2 problems I am having at the moment.
When you are typing, and getting suggestions, but suddenly you click submit then it executes the full search but the problem is that if there is an on going suggestion request there might be the case that they appear on screen when they should not as the full search has priority.
So I would like to cancel (unsubscribe) the request from the suggestions in case there is a submit on the full search.
How can I achieve this with this code?
Another problem is that when I am deleting the search term in the search view and it gets empty, then it clears the adapter but there are cases when I clear the search text, there is still an on going suggestions request and it sets the results but it just clear, so I would like to guarantee that if the user clears the searchview, it cancels all the requests.
Here is the Code I am using.
RxSearchView.queryTextChangeEvents(searchView)
.skip(1)
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
final boolean empty = TextUtils.isEmpty(searchViewQueryTextEvent.queryText());
if (empty) {
//Dont show anything clear adapter
clearAdapter();
}
return !empty;
}
})
.concatMap(new Func1<SearchViewQueryTextEvent, Observable<Object>>() {
#Override
public Observable<Object> call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String searchTerm = searchViewQueryTextEvent.queryText().toString();
boolean submitted = searchViewQueryTextEvent.isSubmitted();
//Hide RecyclerView
//Show loading indicator
showLoading();
Observable<Object> observable;
if (submitted) {
observable = getFullSearch(searchTerm);
} else {
observable = getSuggestionsSearch(searchTerm);
}
return observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnCompleted(new Action0() {
#Override
public void call() {
//Show RecyclerView
//Hide loading indicator
showContent();
}
});
}
})
.subscribe(new Subscriber<Object>() {
#Override
public void onNext(Object object) {
//Set data on recyclerview and notify change.
setData(object);
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
});
}

You might want to try switchMap instead, it just uses the last emited value from the observable.
From the docs:
Returns a new Observable by applying a function that you supply to each item emitted by the source Observable that returns an Observable, and then emitting the items emitted by the most recently emitted of these Observables.
The resulting Observable completes if both the upstream Observable and the last inner Observable, if any, complete. If the upstream Observable signals an onError, the inner Observable is unsubscribed and the error delivered in-sequence.

Related

RxJava - fetch every item on the list and emits one-by-one (any order)

I have a method that returns an Observable<List<Long>>, which are ids of some Items. I'd like to go through this list and download every Item using another method that returns Observable<Item>. currently I'm doing it by below code.
#Override
public Observable<List<Item>> getResponses(List<Long> requests) {
return Observable.from(requests).
flatMap((Func1<Long, Observable<Item>>) restRequest -> getResponseEach(restRequest)).toList();
}
It's working fine, but it returning all the response in on go, I mean when all download get finish then my onNext() get invoked,
Main Question But alternatively I need to emit every response one-by-one(Any Order) once each item fetched successfully from server, so my onNext should be invoked every-time time individually for each item.
How would I do this using RxJava operators?
You have to remove the toList() operator. The toList() will emit only after all the emissions of the upstream have been completed, and it will collect the results and will emit as a Single<List<YourResultObject>>
You can return the observable returned by the flatMap in your code and you will get the results one by one,
public Observable<Item> getResponses(List<Long> requests) {
return Observable.fromIterable(requests)
.flatMap(restRequest -> getResponseEach(restRequest));
}
Now, your getResponses method will return Observable<Item> instead of Observable<List<Item>>
And you can subscribe to this function as follows
getResponses(ids)
.subscribe(new DisposableObserver<Item>() {
#Override
public void onNext(Item item) {
// each item will be received here one by one
}
#Override
public void onError(Throwable e) {
// handle any occured error during the operation
}
#Override
public void onComplete() {
// all operations completed
}
});

RxJava2 - Chaining observables that emit different types

I'm learning RxJava2 and I need to chain three observables:
The first one performs operations on the data:
Completable performOperations(Data data); // performs expensive operations.
The second one uploads data to a server1 and emits percentage progress.
Observable<Integer> uploadToServer1(Data data); // while it performs the upload, it calls several times onNext(progress) and finally calls onComplete().
The third one just informs to a server2 that the upload was done.
Completable informUploadedToServer2(Data data); // just calls a REST API.
I would like to show the progress in my Activity of the second observable and finally show success when the third one finishes successfully. If any of the three observables throws an exception I should show the error in the Activity as well.
I've tried to use concat to chain but it won't compile because uploadToServer1 emits the Integer type and the rest doesn't.
public void upload(Data data, MyCallback callback) {
Observable.concat(performOperations(data).toObservable(), uploadToServer1(data), informUploadedToServer2(data))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<Integer>() {
#Override
public void onNext(Integer integer) {
callback.onProgressChanged(integer);
}
#Override
public void onError(Throwable e) {
callback.onError();
}
#Override
public void onComplete() {
callback.onSuccess();
}
});
}
I've seen that if I change to
Observable.concat(performOperations(data).<Integer>toObservable(), uploadToServer1(data), informUploadedToServer2(data).<Integer>toObservable())
it will work, however, is this the recommended approach?
What is more, what if the first observable emits non-Integers?, for example, a DataDiff object which would describe the modification after a certain operation was performed:
Observable<DataDiff> performOperations(Data data);
How should I subscribe so that I can listen for onNext(Integer) and also onNext(DataDiff) so that the Activity can update the view accordingly?
Thanks.
I would do that in a different way, a more "streamy" approach.
First performOperations(), then use andThen operator to concatenate with the Observable<Integer>, and then you can use concatWith so that after that all the elements from the Observable<Integer> are emitted informUploadedToServer2 is executed. You can then handle the Integer emitted in the subscription consumer, if you observeOn(AndroidSchedulers.mainThread) you can than safely notify your Activity there
performOperations(data)
.andThen(uploadToServer1(data))
.concatWith(informUploadedToServer2(data))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
// notify your Activity here
}
});
In case you needed to intercept the completion of one of the streams, you could use doOnComplete, for instance
performOperations(data)
.doOnComplete(new Action() {
#Override
public void run() throws Exception {
// after performOperations has completed but before
// uploadToServer1 has started
}
})
.andThen(uploadToServer1(data))
// ...
In case performOperations() returned an Observable<DataDiff>, you could use doOnNext to intercept all the events, and then use ignoreElements operator to convert it to a Completable and then use andThen as before
performOperations()
.doOnNext(new Consumer<DataDiff>() {
#Override
public void accept(DataDiff dataDiff) throws Exception {
// handle DataDiff here
}
})
.ignoreElements()
.andThen(uploadToServer1())
// ...

RXJava2 "neverending" Observable

I'm new to RX and got stuck with the next:
I'm using SQLBrite and when query method is called it gives me back a "neverending" Observable which listens to the db changes. When some items are emitted - I need to make some async calls in a loop and give back that items which I received. The sample code:
public Observable<List<Entity>> execute() {
return someManager.getAccount()
.take(1) // I do not need RX to trigger updates when account changed, so use take(1)
.flatMap(accountOptional -> {
if (accountOptional.isPresent()) {
SomeAccount account = accountOptional.get();
return someManager.getEntityDataSource()
.query(new EntitySpecification(account.getId()));
}
return Observable.just(new ArrayList<Entity>()); // No account - no entities
})
.flatMapIterable(entities -> entities) // Here I have a list of entities i I need to make async calls to fill entities with some iiner data (some other entities)
.flatMap(this::loadInnerData)
.toList()
.toObservable();
}
private Observable<Entity> loadInnerData(Entity entity) {
return do some work with entity;
}
The trouble comes when I use toList() - it waits till Observable ends it's job - but it will not be done, as this observable is listening to the db. How can I achieve ability to listen to not stop listening to the "neverending" Observable and loop async calls with RX (as it is done with loadInnerData())?
Maybe try with
return Observable.create(new Action1<Emitter<String>>() {
#Override
public void call(final Emitter<String> stringEmitter) {
// here u can use onNext / onError
stringEmitter.onNext(s);
stringEmitter.onError(e);
}
});
stringEmitter.setCancellation(new Cancellable() {
#Override
public void cancel() throws Exception {
subscription.unsubscribe();
}
});
}
}, Emitter.BackpressureMode.BUFFER);

RXJava - buffer observable 1 until observable 2 emits one item

I want following behaviour:
observableMain should buffers all items until observableResumed emits a value. Then observableMain should emit all buffered and all feature values...
What I do in my activity's onCreate:
PublishSubject<T> subject = ...; // I create a subject to emit items to and to subscribe to
// 1) I create a main observable from my subject
final Observable<T> observableMain = subject
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
// 2) I use a custom base class in which I can register listeners
// for the onResume event and which I can query the isResumed state!
// I call the object the pauseResumeProvider!
// 2.1) I create an observable, it emits a value ONLY if my activity is resumed
final Observable<Boolean> obsIsResumed = Observable
.defer(() -> Observable.just(pauseResumeProvider.isActivityResumed()))
.skipWhile(aBoolean -> aBoolean != true);
// 2.2) I create a second observable, it emits a value as soon as my activity is resumed
final Observable<Boolean> obsOnResumed = Observable.create(new Observable.OnSubscribe<Boolean>()
{
#Override
public void call(final Subscriber<? super Boolean> subscriber)
{
pauseResumeProvider.addPauseResumeListener(new IPauseResumeListener() {
#Override
public void onResume() {
pauseResumeProvider.removePauseResumeListener(this);
subscriber.onNext(true);
subscriber.onCompleted();
}
#Override
public void onPause() {
}
});
}
});
// 2.3) I combine the resumed observables and only emit the FIRST value I can get
final Observable<Boolean> observableResumed = Observable
.concat(obsIsResumed, obsOnResumed)
.first();
// 3) here I'm stuck
// 3 - Variant 1:
Observable<T> observable = observableMain
.buffer(observableResumed)
.concatMap(values -> Observable.from(values));
// 3 - Variant 2:
// Observable<T> observable = observableMain.delay(t -> observableResumed);
// 4) I emit events to my my subject...
// this event is LOST!
subject.onNext("Test in onCreate");
Problem
All items that are send to the subject after the activity is resumed are working, all items before are lost (at least with the delay solution). I can't get my desired behaviour to work. How would I correctly solve that?
Have a source replayed and use delaySubscription to trigger the real subscription.
PublishSubject<Integer> emitNow = PublishSubject.create();
ConnectableObservable<T> co = source.replay();
co.subscribe();
co.connect();
co.delaySubscription(emitNow).subscribe(...);
emitNow.onNext(1);
Edit:
Here is a gist with an operator you can lift into a sequence that can pause and resume emissions from upstream.
Another way to do it is to delay emissions of source (hot) observable until "valve" opens
source.delay(new Func1<Integer, Observable<Boolean>>() {
#Override
public Observable<Boolean> call(Integer integer) {
return valve.filter(new Func1<Boolean, Boolean>() {
#Override
public Boolean call(Boolean isOpen) {
//emits only when isOpen is true
return isOpen;
}
});
}
})
.subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
System.out.println("out: " + integer);
}
});
Gist: Rx Valve

Behaviour of onNext and onComplete

I have an Observable that does something without the need to emit a value. Also I have a list of objects I want the Observable to work with. So for all elements in this list: doSomething()
Observable.from(uris)
.flatMap(new Func1<Uri, Observable<Void>>() {
#Override
public Observable<Void> call(Uri uri) {
return createDoSomethingObservable(uri);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Void>() {
#Override
public void onCompleted() {
Log.d(TAG, "completed");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Void aVoid) {
Log.d(TAG, "next");
}
});
And the method that creates the Observable:
Observable<Void> createDoSomethingObservable(final Uri uri) {
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
//doSomething
subscriber.onNext(null);
subscriber.onCompleted();
}
});
}
Now when I run this with a List with 3 elements I get:
next
next
next
completed
which is good, because that is what I wanted, but I don't know why it's working. First I started to just call onComplete, because in the end the observable does its job and completes. But then of course onNext is never called on the subscriber. The same goes for the other way round.
So my questions are:
Why is onComplete only called for the last list element?
Is there a better way to solve this?
onComplete is called for the last element because that's when the earliest observable in the chain (from(uris)) has finished.
It's expected that your observables emitted from flatMap will call onComplete. Once that's done (and call has returned), then the next emission from from can be worked on. Once from has finished emitting observables, it calls onComplete and the chain is finished, effectively.
I think, that small code helps you to understand behavior of onNext( ) and onComplete().
Suppose, you have an List<Uri>. Let's transform it to Observable<Uri> manually.
public static Observable<Uri> getUries(List<Uri> uriList){
return Observable.create(new Observable.OnSubscribe<Uri>() {
#Override
public void call(Subscriber<? super Uri> subscriber) {
for(Uri uri : uriList){
subscriber.onNext(uri);
}
subscriber.onCompleted();
}
});
}
Or using Lambda expressions:
public static Observable<Uri> getUries(List<Uri> uriList){
return Observable.create(subscriber -> {
for(Uri uri : uriList){
subscriber.onNext(uri);
}
subscriber.onCompleted();
});
}
As you can see, we are iterating input list, and call onNext( ) for every element, and when we finished transforming our List to Observable, we called onComplete()
P.S.
This code just a demonstration, please, never use it to transfor List to Observable. Use operator Observable.from() for it.
UPDATE:
Operator from( ) implementation:
...
while (true) {
if (o.isUnsubscribed()) {
return;
} else if (it.hasNext()) {
o.onNext(it.next());
} else if (!o.isUnsubscribed()) {
o.onCompleted();
return;
} else {
// is unsubscribed
return;
}
}
...
link:https://github.com/ReactiveX/RxJava/blob/1.x/src/main/java/rx/internal/operators/OnSubscribeFromIterable.java#L75-L87
onComplete (same as onError) is called only once during observable chain, it is the way that rxjava is implemented
I think that your approach is correct, so better way is not needed.

Categories

Resources