12

Here's an example scenario:

I have an activity (view) and a presenter for that view. The presenter fetches a list of users from a network API and holds it in memory using a List object. The activity contains different types of fragments to display the content about the users based on User.type. The two fragments (UserType1Fragment and UserType2Fragment) have their own respective presenters too.

The activity's presenter decides what type (I or II) of fragment is shown next based. The fragments' presenters decide how the user object is displayed and handle a button click event called killUser(). This should update the List object in the activity's presenter.

This is where the problem lies:

How do the fragment presents have a reference to the data in activity presenter? The presenters shouldn't directly communicate with each other. Maybe I should abstract out the List into a repository/interactor? How would the List be shared among presenters?

XenoChrist
  • 571
  • 4
  • 13
  • 2
    Persist the data via the model. It's not exactly the same but see the diagram I added in this answer - http://stackoverflow.com/a/41966497/568898. The persistence could be as simple as a static variable (but I don't recommend this) – Jahnold Mar 21 '17 at 13:34
  • @Jahnold Ok, so I implement a repository/interactor which is shared by both the presenters (or is it a singleton)? In this case, let's say the Activity creates a new interactor object and passes it to the Parent Presenter through the constructor. ie in Activity: `onCreate() { mInteractor = new UserInteractor; mPresenter = new Presenter (this, mInteractor); } //and in the fragment: onCreateView() { mFragmentType1Presenter = new FragmentType1Presenter(this, getActivity().mInteractor); }` Does this follow the MVP principle and not cause any issues? Thanks. – XenoChrist Mar 21 '17 at 13:45
  • Don't use the interactor from your Activity in your Fragment. That would couple them together. Just use a separate instance (or if you really have to a singleton) – Jahnold Mar 21 '17 at 14:15
  • @Jahnold If I use separate instances of the interactor, how would they share a model object though? Doesn't this just leave me with a singleton for the interactor as an option? – XenoChrist Mar 21 '17 at 15:34
  • 1
    @XenoChrist do you know more? I'm in the same situation right now ! – Dennis Anderson Mar 31 '17 at 20:58
  • @DennisAnderson Yes! Please check the answer I posted :) – XenoChrist Apr 03 '17 at 08:47

2 Answers2

27

So I ended up implementing something like what @Jahnold recommended. (I'll post the diagram in the link provided for an idea stackoverflow.com/a/41966497/568898 )

Hannes Dorfmann (the guy who created/manages the famous Mosby MVP library : Github link ) also pointed me in this direction.

enter image description here

Implementation

I have a presenter for the main activity and multiple fragments that can be used in that activity. Each fragment has its own presenter. Then I use a repository (search for repository pattern) which is basically stores the models and the business logic. For my use case, I keep this repository as a singleton. The repository provides data in three forms, from an online api, an sqllite database, or a cache stored in memory (basically an arraylist of items). I also have some currentitem int indexes and stuff in this repository, that get updated based on the current state.

Hence the data, the state and the business logic are stored in this shared repository. The presenters and the views are pretty dumb. I don't have much business logic (application specific logic) in presenters. They simply have the logic associated with how the data has to be displayed (view specific logic) and preprocessing in logic them.

Example

Whenever the fragment and activity need to talk to each other (via presenters) when the user clicks a button in a child fragment, the fragment asks its presenter to handleClick, the presenters updates the repository's currentItemSelected data (or something else) and asks the fragment to fire an event (say onbuttonclick) to an interface listener which the activity implements. When the activity gets the event, it asks it's own presenter to handle it and in turn the activity presenter looks for an update in the repository to get the new currentItemSelected.

Extra Info (advanced version):

You can also follow Clean architecture which is sort of a more advanced version of MVP architecture. MVP just deals with the architecture of the views, where as Clean architecture also deals with business logic and data architecture, MVP is just a small part of clean arch which is used to handle the views. Using this, you can break down the mega repo in my case into even further use-cases (or interactors) that handle a specific business logic use-case, and the repository just provides data. So the logic flow is now view-->presenter-->interactor-->repo and back.

Community
  • 1
  • 1
XenoChrist
  • 571
  • 4
  • 13
  • @XenoChrist Thanks for this great answer, but how do you manage fragment replacement in an activity ? Do you do the following : view (onclick) > presenter (setFragment) and save fragment in repo > activity (setFragment (fragment from repo)) ? Or do you go through activity presenter to get the next fragment, and go back again to the activity to replace fragment ? – David Seroussi Sep 16 '17 at 14:39
  • @DavidSeroussi If we consider a proper approach to architecture, presenter shouldn't be aware of Android specific things. As well as other parts, like repositories. In your particular case it should look like this: (view) onClick() calls presenter.onButtonClicked() -> (presenter) onButtonClicked() calls view.replaceFragment() -> (view or your Activity) replaceFragment() do the fragment replacement. It might look overcomplicated, but it is a correct way to stick with Clean Architecture principles. – Volodymyr Buberenko Nov 15 '17 at 14:07
  • I actually implemented it this way but I'm not sure if the activity should tell its presenter that replaceFragment() was called ? Imo it would complicate things and make the code harder to understand – David Seroussi Nov 15 '17 at 17:15
0

you can pass list reference to fragment through newInstance() of fragment. I think the presenters shouldn't directly communicate with each other.