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.
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 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)
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
}));
I'm absolutely new in RxJava, so probably sorry.
There are quite a lot of operators in RxJava and I can't find some that will satisfy me. I want to run method if button was pressed 4 times during last 3 seconds. So I have BehaviorSubject and it call onNext() method every time when button is pressed. And now I need to parse this flow in some way and call onComplete() method in my Observer.
This requires a non-trivial set of operators:
BehaviorSubject<Integer> ps = BehaviorSubject.create();
TestScheduler s = Schedulers.test();
ps.timestamp(s)
.buffer(4, 1)
.takeUntil(b -> {
if (b.size() == 4) {
long diff = b.get(3).getTimestampMillis()
- b.get(0).getTimestampMillis();
if (diff < 3000) {
return true;
}
}
return false;
})
.doOnCompleted(() -> System.out.println("-->> Four clicks!"))
.subscribe();
System.out.println("Sending 3 clicks");
ps.onNext(1);
ps.onNext(2);
ps.onNext(3);
s.advanceTimeBy(4, TimeUnit.SECONDS);
System.out.println("Sending 4 clicks");
ps.onNext(4);
ps.onNext(5);
ps.onNext(6);
ps.onNext(7);
What I'd do is timestamp each source value, do a sliding buffer (after each source item, up to 4 entries), use takeUntil to check if the time difference between the buffer's first and last item is within 3 seconds and return true to complete the stream.