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)
Related
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.
I saw a whole lot of posts for having Rx delaying each emission of an event : How to make countdown timer with RxJS Observables?, How to use RxJava Interval Operator, Adding delay between Observable Items RxJava, RxJava delay for each item of list emitted, and others.
Though, I didn't see any for chaining with different delays.
Basically, I have a Textview and a list of letters, and I'd like to :
set the text to the first letter
wait 1500ms
set the text to null
wait 500ms
set the text to the second letter
wait 1500ms
set the text to null
wait 500ms
repeat for the entire list
A code implementation could maybe look somehow like this (but I guess doThing() is nonsense in Rx, and delay() is not meant for this) :
Observable.fromArray(new String[]{"A", "B", "C", "D", "E"})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.delay(500L, TimeUnit.MILLISECONDS)
.doThing((String i) -> {
textView.setText("");
Log.d("XXX", "MainActivity :: onCreate (110): letter="+ i);
})
.delay(1500L, TimeUnit.MILLISECONDS)
.doThing((String i) -> {
textView.setText(i);
Log.d("XXX", "MainActivity :: onCreate (110): letter="+ i);
});
How can I achieve this with Rx ?
EDIT : I could use the answer from rxjava delay: How to get variable delay on each item emitted from a list? with a list of letters where one letter on two is special (null maybe ?), but it seems overcomplicated.
Sequence: A (1500ms) null (500ms) B (1500ms) null (500ms) C (500ms) null (1500ms)
textAnimationDisposable = Observable.fromArray("A", "B", "C")
.concatMap(string ->
Observable.merge(
Observable.just(string),
Observable.just("").delay(1500, TimeUnit.MILLISECONDS)
)
.concatWith(Observable.<String>empty().delay(500, TimeUnit.MILLISECONDS))
)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(string -> textView.setText(string));
The last solution you linked is quite useful for controlling the delay of each item separatly.
I have a list of items, for each item i wan to do an async job and then update the UI. I am using rxjav for doing async task. How can i loop through the list and do this. I tried with Observable.FromArray thought it didnt work
Observable.fromArray
flatMap is your answer. You will pass your list to Observable, in flatMap each item from the list will be passed so that you can do the async task. Finally, you can update the UI.
Observable.fromArray(yourArrayList)
.flatMap(item -> doAsyncTask)
.flatMap(item -> updateUI)
.subscribe();
You can refer to marble diagram on how flatMap works.
You'll need to use .flatMap() for this. Flatmap will allow you to perform an action and emit an observable for each item. You can use this to perform async operations on each item in the list. The flat map operation can return items of the same type or another type.
Example:
Suppose you have a list of items of type Object1. You want to upload these to a REST API. The API will return the uploaded object in the response.
// Create observable
Observable<Object1> uploadObjects = Observable.fromIterable(objectsToUpload)
.flatMap(object -> {
// Calls uploadObject on each item in the list
return MyAPI.uploadObject(object);
})
.subscribeOn(Schedulers.io()) // Performs the action on background thread
.observeOn(AndroidSchedulers.mainThread()); // Returns result on UI thread
// Subscribe to observable
Disposable subscription = uploadObjects
.subscribe(returnedObject -> {
// Called for each returned object
// Update UI here
}, throwable -> {
// Handle error here
}, () -> {
// Runs after all items completed
});
Suppose MyAPI.uploadObject() takes an Object1 as an argument but returns a different kind of object: type Object2. If this is the case, then the uploadObjects observable simply needs to be of type Observable<Object2>. Each returnedObject in the subscribe block with then be of type Object2.
Some nice examples from Dan Lew can be found here.
I need to close socket connection in my observable before RxLifecycle dispose it. How can I do that?
if you want to do an action after all, just before the subscriber unsubscribe from the observable you can use operator doOnUnsubscribe
#Test
public void testDoOnUnsubscribe() {
Integer[] numbers = {0, 1, 2, 3, 4};
Observable.from(numbers)
.doOnUnsubscribe(() -> System.out.println("Last action must be done here"))
.subscribe(number -> System.out.println("number:" + number),
System.out::println,
() -> System.out.println("End of pipeline"));
}
It should print in this order
number:0
number:1
number:2
number:3
number:4
End of pipeline
Last action must be done here
You could try using doFinally
Calls the specified action after this Observable signals onError or onCompleted or gets disposed by the downstream.
http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#doFinally-io.reactivex.functions.Action-
one can try this too in case if you're iterating objects using filter and map for combining result.
.doOnTerminate(() -> Log.d(LOGGER, "terminated"))
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
}));