MVP for Activity with multiple Fragments

13,477

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);
    }

}
Share:
13,477
DJ-DOO
Author by

DJ-DOO

Updated on June 22, 2022

Comments

  • DJ-DOO
    DJ-DOO about 2 years

    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?

  • Jenya Kirmiza
    Jenya Kirmiza about 7 years
    sir, can you explain why mWeakView is a WeakReference, for what reasons?
  • fernandospr
    fernandospr about 7 years
    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
    fernandospr about 7 years
    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.
  • Jenya Kirmiza
    Jenya Kirmiza about 7 years
    I'm doing attach/detach thing. also when app is paused I'm stopping all network calls, don't know if it's correct
  • Shobhit Puri
    Shobhit Puri about 7 years
    @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?
  • fernandospr
    fernandospr about 7 years
    @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.
  • Shobhit Puri
    Shobhit Puri about 7 years
    @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?
  • fernandospr
    fernandospr about 7 years
    @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.
  • eRaisedToX
    eRaisedToX about 7 years
    @fernandospr what is mWeakView.get();...?
  • fernandospr
    fernandospr about 7 years
    @eRaisedToX mWeakView.get() returns the MyPresenter.View object that is set in the constructor.
  • Rishav Chudal
    Rishav Chudal almost 7 years
    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?
  • fernandospr
    fernandospr almost 7 years
    @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.