38

I have an Activity with two Fragments in it.

The activity (MainActivity) retrieves data from an open weather api. I have implemented MVP for this in which: Model contains all the response objects from the API
View is the Activity
Presenter contains MainPresenter, MainPresenterImpl, MainView, GetDataInteractor and GetDataInteractorImpl.

So, the activity gets the data from the web service. Both fragments will display data from the data retrieved in the activity.

What is the best practice using MVP in this situation? I know how to pass data between fragments <-> activity via interface/callbacks, my question is does this behaviour change when implementing MVP?

Willi Mentzel
  • 21,499
  • 16
  • 88
  • 101
DJ-DOO
  • 4,219
  • 15
  • 48
  • 91
  • 1
    Just a thought: I'd consider fragments to count as Views (in regard to MVP), hence I wonder if it would be odd to have one presenter reference multiple views (or rather: their interface callbacks) to display different data in the view most appropriate for that? I'd think a presenter needs to decide/direct which view displays which data?P.s. Apparently multiple presenters for one view is a valid approach so maybe the other way around works as well: http://stackoverflow.com/a/2068/1041533 – AgentKnopf Dec 17 '15 at 20:28
  • 2
    @AgentKnopf actually, as stated here http://programmers.stackexchange.com/a/261351/206366 in MVP each presenter is responsible for presenting one view. The only way that a presenter can present multiple views is if the different views are merely different implementations of a single view interface that binds to the presenter. – Ari Aug 03 '16 at 11:51
  • @Ari thank you for the follow up - that does indeed make sense! – AgentKnopf Aug 28 '16 at 08:26

1 Answers1

17

The activity/fragments should be considered as just the view in the MVP model. This means that they should just show data and receive user interactions. It is ok to communicate activity and fragments via interface/callbacks.

But, it is not an activity/fragment responsibility to call the API services.

The presenter should be responsible to call the api services.

So, the presenter should expose a method like loadXXX, internally it would make the call to the service. When the response is received, the presenter should call view.showXXX with the results of the service. The activity/fragment should call this loadXXX method and implement the showXXX.

Usually, the presenter is created or injected into the activity/fragment. The activity/fragment has to implement an interface exposed by the presenter, and the presenter holds a weak reference of this interface, so that it can callback.

When the user interacts with the screen, for example, an onClick on a button, the activity/fragment calls the corresponding method in the presenter, e.g. presenter.loadUserDetails() the presenter tells the view to show as loading, e.g. view.showAsLoading() because it has to do its stuff: maybe validating something or loading data from an api service and finally callback with the results to the view, e.g. view.showUserDetails(userDetails).

To summarize, an example, in code of the various parts of MVP:

Activity/Fragment represents just the View of MVP:

public class MyActivity extends AppCompatActivity implements MyPresenter.View {
    private MyPresenter mPresenter;

    public onCreate() {
        ...
        mPresenter = new MyPresenter(this); // Or inject it and then set the view.
    }

    public void onClick(View v) {
        mPresenter.loadXXX(param1, param2);
    }

    // MyPresenter.View methods

    public void showAsLoading() {
        ...
    }

    public void showUserDetails(UserDetails userDetails) {
        ...
    }
}

Model:

public class UserDetails {
    ...
}

Presenter:

public class MyPresenter {

    private WeakReference<MyPresenter.View> mWeakView;

    public MyPresenter(MyPresenter.View view) {
        mWeakView = new WeakReference(view);
    }

    public void loadXXX(String param1, String param2) {
        MyPresenter.View view = mWeakView.get();
        if (view != null) {
            view.showAsLoading();
            // Do stuff, e.g. make the Api call and finally call view.showUserDetails(userDetails);
        }
    }

    interface View {
        void showAsLoading();
        void showUserDetails(UserDetails userDetails);
    }

}
fernandospr
  • 2,541
  • 1
  • 18
  • 37
  • sir, can you explain why mWeakView is a WeakReference, for what reasons? – Jenya Kirmiza Apr 05 '17 at 13:13
  • 1
    This is to avoid retaining a reference to the Activity/Fragment. The presenter makes async requests to APIs. If you press back or finish the activity while the presenter is making the request and you don't use a WeakReference, the presenter will retain the Activity/Fragment memory (retaining all the views and members of that Activity/Fragment). Instead of using a WeakReference, it's also common to expose an attach and detach method in the presenter. After instantiating the presenter you should call the attach method and when Activity/Fragment's onDestroy is called, you should call detach. – fernandospr Apr 05 '17 at 13:37
  • If you don't use a WeakReference nor the attach/dettach approach and your activity/fragment was destroyed before the presenter's request has finished, you could also run into problems when the request finishes, because it will try to update something on the destroyed activity/fragment. – fernandospr Apr 05 '17 at 13:40
  • I'm doing attach/detach thing. also when app is paused I'm stopping all network calls, don't know if it's correct – Jenya Kirmiza Apr 05 '17 at 14:23
  • @fernandospr Thanks for the helpful answer. Do you think that one should have different presenter for activity and fragment. Since an activity like the one with tabs can have numerous fragments with different functionality. Is it cleaner for each fragment to have its own presenter? In that case if fragment's presenter has to communicate to activity's what is the best way to do it? I know one can call activity method via an interface which fragment implements, which in turns can call activity's presenter. However is it too much? – Shobhit Puri Apr 24 '17 at 16:26
  • @ShobhitPuri You can use a presenter per fragment if it is worth or you can just use only one presenter with multiple fragments. You should always try to do just UI stuff in your activities/fragments and call the presenter from activity or the fragments (if there are fragment) when you have to perform an operation. To communicate from the fragment to the activity you should use the common listener approach. – fernandospr Apr 24 '17 at 18:15
  • @fernandospr Thanks! That's helpful. If there is a code wherein lets say one is programatically adding UI elements to existing layout, so that goes in activity too, right? – Shobhit Puri Apr 24 '17 at 18:38
  • 3
    @ShobhitPuri Yes, absolutely. Everything UI related must be done in the View layer: activities and fragments. As a rule of thumb, you shouldn't have any android imports in the presenter. – fernandospr Apr 25 '17 at 12:57
  • @fernandospr what is `mWeakView.get();`...? – eRaisedToX May 24 '17 at 09:11
  • @eRaisedToX `mWeakView.get()` returns the `MyPresenter.View` object that is set in the constructor. – fernandospr May 24 '17 at 15:54
  • What if I have multiple nested fragments? Should I create presenter for each fragments or I can call connection class from fragments directly if I have to follow MVP pattern? – Rishav Chudal Sep 10 '17 at 07:20
  • @RishavChudal If you want each fragment to be totally independent and each fragment has to, for example, make a network request, then I'd suggest you to have a presenter for each fragment. If not, I'd say you can just have one "main" presenter in your "main" activity/fragment and when any action is performed on the nested fragment you can always tell your container activity/fragment about it and the container will call the corresponding method of the presenter. – fernandospr Sep 10 '17 at 16:03