Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rxjava interval() reset time after some event

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?

like image 238
Chris.Zou Avatar asked Mar 21 '16 06:03

Chris.Zou


2 Answers

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.

like image 83
Patrick Bacon Avatar answered Nov 17 '22 14:11

Patrick Bacon


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();
    }
}
like image 23
paul Avatar answered Nov 17 '22 13:11

paul