Longrunning tasks #124

Closed
PaulWoitaschek opened this Issue Mar 23, 2016 · 5 comments

Projects

None yet

3 participants

@PaulWoitaschek

I still have problems in understanding how long running tasks can be handled with this library.

Assume I have an media player which has an Observable that emits the current playback position.
My approach here is to subscribe to that observable when the view gets attached and unsubscribe when the view gets detatched.

However, FragmentMvpDelegateImpl shows that the view is attached between onViewCreated and onViewDestroyed. But that also means that the view gets constantly updated when the screen is off which is obviously bad.

Is there a way to handle these kind of scenarios using mosby?

@sockeqwe
Owner

The same way as you would do it without mosby. Let's say you have an MediaPlayer Observable. How would you implement it without mosby? You would subscribe / unsubscribe in Fragment.onPause() and Fragment.onResume().
Usually there is no need that a mosby presenter has "lifecycle callbacks".

Furthermore, I don't think that presenter is responsible for lifecycle. Why? Because lifecycle is already a complex topic. If you start to move lifecycle logic in your presenter, than you basically have just moved that spaghetti code from activity to presenter. If you just need to do something onStart() then simple call presenter.doSomething() in Fragments.onStart(). I think there is the need of a separated component responsible for lifecycle handling, the activity, and not the presenter. The presenter should just be responsible to coordinate the view and to be the bridge to the "business logic" to retrieve data that should be displayed in the view. Given that "definition" of a presenter, I don't see a reason why the presenter needs lifecycle callbacks. All the presenter needs to know is whether or not a view is attached to the presenter. It should be that simple. Actually this is one of the arguments why developers are against fragments (complex lifecycle) and prefer "custom views" because custom views lifecycle are only two events: ViewGroup.onAttachedToWindow() and ViewGroup.onDetachedFromWindow().

With that said, there might be some cases when the presenter needs lifecycle events, but mainly because the underlying "business logic" needs lifecycle events. Think about an app that uses GPS. To not drain the battery too much you would stop GPS tracking when Fragment.onPause() and resume when Fragment.onResume(). So again, it's not the presenter who needs onPause() and onResume() lifecycle but rather the "business logic", in that case the GPS tracker, that needs that lifecycle events. So here it might make sense to forward this lifecylce events to the presenter, and then the presenter starts/stops GPS tracking. That would be one of the few scenario where I would say that it would make sense to add lifecycle callbacks to the presenter. But, I think there is a better way for doing that in the GPS example. As already said, is not the responsibility of the presenter to handle lifecycle events. Actually, there is already a component that is responsible for lifecycle events: the fragment / Activity.

So instead of forwarding Activity.onPause() and Activity.onResume() to the presenter, just do something like this:

class TrackingActivity extends MvpActivity implements TrackingView {
      private GpsTracker tracker;

      public onCreate(){
            tracker = new GpsTracker(this); // might need a context
            ...
      }

     public void onPause(){
         tracker.stop();
     }

    public void onResume(){
         tracker.start();
     }

    @Override
    public void createPresenter(){
          return new TrackingPresenter(tracker);
     }
}

And then the presenter takes the GPS tracker as constructor parameter and registers himself as listener to the GPS tracker (business logic). By doing so you have a presenter that is just responsible to update the View as the original definition of the presenter recommends. Moreover, you still have this clear separation of concerns: Presenter, coordinates the view and listens for data to be displayed on the view from business logic, and a dedicated lifecycle component (the activity or fragment).

So I think you could do exactly the same thing with your music player observable. So either add onPause() and onResume() callbacks to your Presenter, but actually you need this lifecycle information for your "business logic", the media playback observable. So you could do the same as GPS Tracker example from above. Make a class like this:

class UpdateMediaPlayerObservable {

      Observable  realMediaPlayerObservable = ...;
      boolean updatedProgress;

     Observable getObservable(){
         return realMediaPlayerObservable.filter( (item) -> updateProgress);
      }
}

And then in your Fragment:

class MediaPlayerFragment extends MvpFragment implements MediaPlayerView {
      private UpdateMediaPlayerObservable mediaPlayer;

     public void onPause(){
         mediaPlayer.setUpdateProgress(false);
     }

    public void onResume(){
        mediaPlayer.setUpdateProgress(true);
     }

    @Override
    public void createPresenter(){
          return new MediaPlayerPresenter(mediaPlayer);
     }
}

The trick here is that the presenter still is subscribed when the screen is off, but don't update the "ui" because of the .filter() operator. Alternatively, as already said you can forward Fragment.onPause() and Fragment.onResume() to Presenter.onPause() and Presenter.onResume().

if you really need onResume() and onPause() events in all of your Presenters you could implement your own PausableFragmentMvpDelegateImpl that interacts with an PausablePresenter and use this delegate instead of the default FragmentMvpDelegateImpl. See delegation section here (scroll down until delegation header). But I do think that usually the presenter should not be lifecycle aware (code smells).

Hope that helps

@sockeqwe sockeqwe closed this Mar 23, 2016
@PaulWoitaschek

Thanks for the feedback. That was very helpful!

@farmazon3000

Isn't this breaking separation of layers a little bit, i.e. View bypasses the Presenter talks with Model.

@farmazon3000

Yes this is where I came from. And this is why I asked the question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment