RxJava - How to stop a running task when new item emitted - android

I am building searching feature for my Android app with RxJava. When users type/change query characters in an EditText, an emitter will emit new query text and I search around my database to get results that match the query.
getEditTextObservable()
.subscribeOn(Schedulers.computation())
.debounce(500, TimeUnit.MILLISECONDS)
.filter {
!it.isNullOrEmpty()
}
.map {
//start searching
getResultsInDatabase(it) //this function takes a long time to complete
}.observeOn(AndroidSchedulers.mainThread())
.subscribe{
//render results on screen
}
The method getResultsInDatabase(string:String) take a long time to complete, when the query text changed, I want to stop method getResultsInDatabase(string:String) (in case it is running with previously emitted query) to run it again with new query. How can I do to achieve that? I would appreciate your helps. Thank you for reading my question.

You need a switchMap.
getEditTextObservable()
.subscribeOn(Schedulers.computation())
.debounce(500, TimeUnit.MILLISECONDS)
.filter { !it.isNullOrEmpty() } //Be careful with this. Should clearing text clear results too?
.switchMap {
Observable.fromCallable { getResultsInDatabase(it) }.subscribeOn(Schedulers.io())
}.observeOn(AndroidSchedulers.mainThread())
.subscribe{
//render results on screen
}

You can use valve to pause a stream in RxJava. Based on the value of it, the stream continues to emit or will be kept in a paused state.

Related

RxJava PublishSubject+Debounce: the second item not emited

I want to use PublishSubject + debounce (in subscribe logic) for emit my items with delay. This is my code:
Subscription logic:
notificationSubject = PublishSubject.create<Notification>()
notificationSubject
.debounce(300, TimeUnit.MILLISECONDS)
.doOnIOSubscribeOnMain() // ext. fun, I hope you understand it
.subscribe {
displayNotification(it)
}
And emit objects logic:
showNotification(obj1)
showNotification(obj2)
// ...
fun showNotification(notification: Notification) {
notificationSubject.onNext(notification)
}
But on subscribe I receive only first emitted item (obj1). And if I emit two objects (obj3, obj4) again I receive only first of emitted item (obj3).
How to fix it?
Debounce is a lossy operator that skips items emitted too close to each other. You can't use that for addressing your requirements.
You could zip with an interval instead:
notificationSubject.zipWith(Observable.interval(300, TimeUnit.MILLISECONDS), (a, b) -> a)

RxJava - sequential call of two methods in background and return values in main thread

I would like to call getNote in background.
After I get result which is a Note object and still in background I would like to take two Note's values - text_encrypted and date.
After all I would like to return them to next step and in main thread set values on two textViews.
How could I achive this? Here is my code below.
Observable.fromCallable(() -> NotesDataBase.getNote(id))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(notes -> {
new Object[]{
GeneratorAES.decrypt(notes.text_encrypted),
CalendarUtils.showArticleTime(notes.date)
};
})
.subscribe(objects -> {
((TextView) findViewById(R.id.text2)).setText(objects[0]);
((TextView) findViewById(R.id.text1)).setText(objects[1]);
});
I assume I am doing something wrong in first flatMap but I am not sure.
This should not compile since flatMap() requires you to return an observable from your lambda. Additionally observeOn() will change the thread for all following operations of the chain.
Instead you have to call observeOn() after your operations and only map() the values. To make it nicer, you could also use Pair instead of Object[]:
Observable.fromCallable(() -> NotesDataBase.getNote(id))
.subscribeOn(Schedulers.io())
.map(notes -> Pair.create(
GeneratorAES.decrypt(notes.text_encrypted),
CalendarUtils.showArticleTime(notes.date)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(pair -> {
((TextView) findViewById(R.id.text2)).setText(pair.first());
((TextView) findViewById(R.id.text1)).setText(pair.second());
});

RxJava emit only when emitter stops

In an Android app, I'd like to refresh the list only once the user has stopped selecting a list of items in a List. So in effect, I'd like to the observer to be informed only once the producer has stopped emitting for at least 500ms.
Right now I have something like the following:
Subject<Object> _bus = PublishSubject.create().toSerialized();
...
_bus.onNext(new Event());
...
_bus.delay(500, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.observeOn(Schedulers.computation())
.subscribe(event -> {
// Do something with event
}));
This is fine, except it emits at 500 ms intervals even if the source is still emitting. I'd like to wait for 500ms to see if the source has stopped calling onNext() and only then emit.
Is this possible?
So basically you need debouncing with buffer. There is article which should helper you.
And kick off sample from that article:
Observable<Object> tapEventEmitter = _rxBus.toObserverable().share();
Observable<Object> debouncedEventEmitter = tapEventEmitter.debounce(1, TimeUnit.SECONDS);
Observable<List<Object>> debouncedBufferEmitter = tapEventEmitter.buffer(debouncedEventEmitter);
debouncedBufferEmitter.buffer(debouncedEventEmitter)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<Object>>() {
#Override
public void call(List<Object> taps) {
_showTapCount(taps.size());
}
});
I think you have to used debounce operator instead of delay eg.
_bus.debounce(500, TimeUnit.MILLISECONDS
.distinctUntilChanged()
.observeOn(Schedulers.computation())
.subscribe(event -> {
// Do something with event
}));

RxJava pattern for returning cold results, doing more work, then returning hot results

I'm learning RxJava so please be gentle. I've watched the tutorials, done the reading, searched SO, however, I'm still having some problems transforming my AsyncTaskLoader. For some reason, I can't find a pattern of operators to achieve my task (although I think it's a common one). What I'm trying to do is the following: return an Observable my fragment could subscribe to. The observable should do the following on subscribe:
1) Fetch data from the local database by doing 2 queries, running some logic and returning results;
2) Fetching data from API;
3) Synchronising the new API data with the database;
4) Repeating step one and returning results;
So far I've transformed my db calls and my API calls to return observables. I'm trying to understand how I can emit the cold results and continue with the chain. I could probably keep the two operations separately, and use the same subscriber to subscribe to both? But I'm not sure how that would work if my new loader-replacement class returns an observable... Also I don't really need to process the results from the second observable - I just need for the first one to replay when the second one finished.
So far I have the following:
public Observable<StuffFetchResult> getColdStuff() {
return Observable.zip(mDataSource.listStuff(), mDataSource.listOtherStuff(),
(stuff, moreStuff) -> {
List<Stuff> mergedList = new ArrayList<>();
// do some merging stuff
return new StuffFetchResult(mergedList);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Assume I also have getHotStuff() that will do the API call and the synchronisation with the database, if that's the right approach, and return the same Observable. However, I'm stuck on the next step - how can I restart the first observable to replay once hotStuff has completed, without adding another subscriber?
EDIT:
I've made some progress and I think all I need now is to join it all up. I have my two methods:
1) getColdStuff() is pretty much as described above
2) getHotStuff() will do call to the API, synchronise with the database, and return an Observable. The idea was to call getColdStuff() again after getHotStuff() has finished in order to refresh the UI, so actual result returned from getHotStuff() can be ignored. All it needs to do is to trigger getColdStuff() once done.
I've tried the suggestion in the answer to and created the following:
BehaviorRelay<Observable<StuffFetchResult>> callSequence = BehaviorRelay.create();
Observable<StuffFetchResult> valueSequence = Observable.switchOnNextDelayError(callSequence.toSerialized());
valueSequence.subscribe(new Subscriber<StuffFetchResult>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(StuffFetchResult result) {
// UI stuff
}
});
callSequence.call(loader.getColdStuff());
I can subscribe to valueSequence here and use callSequence.call(loader.getColdStuff());, which will run the first method and produce results in onNext() of my subscription, which I can use for my UI. However, I'm not sure how to run getHotStuff() in parallel and also do a different action on it when it returns. Also getHotStuff() returns a different type of Observable so I can't really use the same callSequence?
EDIT 2
Using two subscribers, I can achieve the required behaviour I think. Not really sure if that's the right way to go about it though.
loader.getHotStuff()
.subscribeOn(Schedulers.io())
.subscribe( new Subscriber<Object>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(Object stuffWeDontCareAbout) {
callSequence.call(loader.getColdStuff());
}
});
if i understand your scenario correctly, you may want something like that -
BehaviorSubject<Observable<T> callSequence = BehaviorSubject.create();
Observable<T> valueSequence = Observable.swithOnNextDelayError(callSequence.toSerialized());
your subscriber will be listening to the valueSequence, and whenever you need to "restart", you will call this -
callSequence.onNext(call.cache()); // *call* is Observable<T>
(i leave the .subscribeOn/.observeOn configuration to you)

Observable seems to not call onNext after a while

I'm using RxAndroid library to process a list of items using subscriber / observable pattern.
My problem is that, when an item is processed, there is a progress bar that needs to be updated. But after processing 16 items, it seems that the observable is not calling onNext method until the rest of the items( 90) are processed and then calls 90 times onNext method. Why is this happening? can this be a memory issue?
Code below.
Subscriber:
public void startSingleRecognition(int id, int position) {
mAdapter.updateItemProgress(0, position);
Uri imageUri = Uri.parse(getHpCard(id).getUrlImage());
final int[] i = {0};
mSubscription = mRecognitionUtils
.getRecognitionObservable(imageUri, configurations)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
abbyResult -> mAdapter.updateItemProgress(++i[0], position),
e -> e.printStackTrace(),
() -> mAdapter.updateItemProgress(-1, position));
}
Observable:
public Observable<AbbyResult> getRecognitionObservable(Uri imageUri,
ArrayList<Configuration> configurations) {
return Observable.from(configurations)
.flatMap(
configuration -> Observable.just(recognize(imageUri, configuration, this)));
}
The method recognize does hard work processing images, my first thought was that this method is consuming a lot of memory and the observable cannot deliver the processed item to the subscriber until all method calls are done. But I'm not really sure, can anyone confirm this?
Thanks!
Well, I think I have solved it! The issue was using flatMap instead of concatMap. Here it is well explained: http://fernandocejas.com/2015/01/11/rxjava-observable-tranformation-concatmap-vs-flatmap/

Categories

Resources