MVP for Activity with multiple Fragments
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);
}
}
DJ-DOO
Updated on June 22, 2022Comments
-
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 theActivity
Presenter
containsMainPresenter
,MainPresenterImpl
,MainView
,GetDataInteractor
andGetDataInteractorImpl
.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 about 7 yearssir, can you explain why mWeakView is a WeakReference, for what reasons?
-
fernandospr about 7 yearsThis 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 about 7 yearsIf 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 about 7 yearsI'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 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 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 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 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 about 7 years@fernandospr what is
mWeakView.get();
...? -
fernandospr about 7 years@eRaisedToX
mWeakView.get()
returns theMyPresenter.View
object that is set in the constructor. -
Rishav Chudal almost 7 yearsWhat 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 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.