New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Longrunning tasks #124

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

Comments

Projects
None yet
3 participants
@PaulWoitaschek

PaulWoitaschek commented Mar 23, 2016

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

This comment has been minimized.

Show comment
Hide comment
@sockeqwe

sockeqwe Mar 23, 2016

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

Owner

sockeqwe commented Mar 23, 2016

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

This comment has been minimized.

Show comment
Hide comment
@PaulWoitaschek

PaulWoitaschek Apr 3, 2016

Thanks for the feedback. That was very helpful!

PaulWoitaschek commented Apr 3, 2016

Thanks for the feedback. That was very helpful!

@farmazon3000

This comment has been minimized.

Show comment
Hide comment
@farmazon3000

farmazon3000 May 9, 2016

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

farmazon3000 commented May 9, 2016

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

@farmazon3000

This comment has been minimized.

Show comment
Hide comment
@farmazon3000

farmazon3000 May 9, 2016

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

farmazon3000 commented May 9, 2016

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