sockeqwe / mosby

A Model-View-Presenter / Model-View-Intent library for modern Android apps
http://hannesdorfmann.com/mosby/
Apache License 2.0
5.49k stars 841 forks source link

How to share the intent observable? #246

Closed roshanpisharody closed 7 years ago

roshanpisharody commented 7 years ago

Hi, In My app User clicks a button to increment the counter by one. But the user can click the button rapidly to increase the count. As the user clicks a network request would get fired. But i don't want to fire network request every time the button get clicked. But I also want the UI get updated as soon as the event happens. I tried to use share operator but then it doesn't work. So how should I approach this problem.

lenguyenthanh commented 7 years ago

You can use Multicasting in RxJava. Like:

val buttonStream = view.buttonClickedTream().share()
buttonStream.subscribe { updateUI() }
buttonStream.flatMap(doNetworkStuff()). subscribe { updateUI() }

You can take a look at this blog: http://blog.danlew.net/2016/06/13/multicasting-in-rxjava/

Edit: Sorry, I didn't read your issue carefully. Can you tell us more how share operator doesn't work?

roshanpisharody commented 7 years ago

The intent method in the presenter takes the observable returned by the view. I added a share operator on the button click observable so that I could have two subscribers one for updating ui immediately and other for network operation. But with that setup the UI updation code never executes.

sockeqwe commented 7 years ago

There are may ways to do this. share() could be used like this in Presenter.bindIntents():

public void bindIntents(){
  Observable<?> buttonClickIntent = intent(View::butonClickIntent).share();
  Observable<Foo> directUpdate = buttonClickIntent.map( /* transform to state to update UI immediately */ );
  Observable<Foo> networkRequest = buttonClickIntent.map( /* execute network request */):

  Observable<Foo> allObservables = Observable.merge(directUpdate, networkRequest);
  subscribeViewState( allObservable,  View::render);
}

Alternatively, you could also do something like this:

public void bindIntents(){
  Observable<Foo> observable = intent(View::clickIntent)
        .flatMap(click -> {
               if (shouldRunNetworkRequest()) 
                   return networkApi.getFoo()
                                    .startWith(Foo.initialValue());  // directly Update UI before starting Http request
               else
                   return Observable.just(new Foo() ); // Directly update UI, no networkRequest
         });
  subscribeViewState( observable,  View::render);
}

I hope this gives you an idea on how this could be achieved. There are also other ways, it really depends on what exactly you want to implement.

roshanpisharody commented 7 years ago

Thanks. Your Suggestion works.

sockeqwe commented 7 years ago

Great. Please consider asking future queations like this on stackoverflow because stackoverflow is much better indexed compared to github issue tracker by search engines like google and therefore easier to find for other Mosby users with same / similar question. There is a mosby tag on stackoverflow I regularly keep looking at for new questions. Also this question is not really Mosby related, its more aboit RxJava chain construction.

I would also appreciate if you could copy over this question & answer to stackoverflow​. I'm sure you are not the first nor the last developer facing such a problem. Thanks.

roshanpisharody commented 7 years ago

I would definitely going to do that. But stackoverflow Blocked me for one day. I will post this question tommorow.