I'm making an Android app with RxJava, in one of the page, I have a button, when pressed, the page will do a refresh. And I also want a auto-refresh for every 10 seconds, if user haven't pressed the button during that period. But when user clicks the button, I want the auto-refresh action to happen 10 seconds later after the click. Rather than continuing its own 10-second interval.
For example, at second 0, the app do a auto-refresh, then at second 3, user pressed the button. Then the auto-refresh should happen at second 13, second 23, etc.
I know that there is an interval()
operator that emits items at a certain interval. But it seems there is no way to "reset" the start time. Its kinda like unsubscribe and subscribe to the interval()
Observable again. A piece of code would be like
Observable<Long> intervalObservable = Observable.inteval(10, TimeUnit.SECONDS)
RxView.click(refreshButton).map(ignored -> 0L)
.merge(intervalObservable)
.subscibe(ignore -> performRefresh());
If there is a way to "unmerge" the intervalObservable
, then I can unmerge it in onNext
and then merge it again. But it seems there isn't.
How can I achieve this?
You can do this pretty nicely with the switchMap
operator. Each time the button is pressed it will switch to a new subscription of the interval observable - meaning it will start over again. The previous subscription is dropped automatically so there won't be multiple intervals running.
Observable<Long> intervalObservable = Observable.interval(10, TimeUnit.SECONDS);
RxView.clicks(refreshButton)
.switchMap(ignored -> {
return intervalObservable
.startWith(0L) // For an immediate refresh
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(x -> performRefresh());
})
.subscribe();
The startWith
adds an immediate value to the interval (causing the refresh immediately when the button is clicked), and the observeOn
makes sure the refresh happens on the main thread (important since the interval will emit on a background thread).
Update: vman noticed that this implementation only starts refreshing after the user clicks the button the first time. The following alternative will refresh immediately upon subscription, then every 10 seconds, until the button is clicked – at which point it will refresh immediately again, then continue updating every 10 seconds.
Observable<Long> intervalObservable = Observable.interval(10, TimeUnit.SECONDS)
.startWith(0L) // For an immediate refresh
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(x -> performRefresh());
Observable<Long> buttonClickedObservable = RxView.clicks(refreshButton)
.map(e -> 0L) // To make the compiler happy
.switchMap(ignored -> Observable.error(new RuntimeException("button pressed")));
Observable.merge(intervalObservable, buttonClickedObservable)
.retry()
.subscribe();
In this implementation an observable that does the refreshing gets subscribed to immediately (leading to an immediate refresh, and repeating every 10 seconds), It's merged with an Observable that will error whenever the refresh button is clicked. The retry
at the end will cause the whole thing to get resubscribed to on an error (button click) – causing it to all start over again.
Easiest solutions would not be that every time that you click the button you unsubscribe the old observable and subscribe again?
Subscription subscription= null;
Observable<Long> interval = nul;
public void subscribe(){
interval = Observable
.interval(10000L, TimeUnit.MILLISECONDS);
subscription = interval.subscribe(item -> System.out.println("doing something"));
}
public void eventClick(){
if(clickButtin){
subscription.unsubscribe();
subscribe();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With