Android MVP Strategy

I port my applications to MVP. Understood about the static template template from konmik

This is my short MVP strategy. For clarity, most listeners are templates and MVPs. This strategy helped me change the orientation of my background processes. An action is correctly restored from a normal pause compared to a pause that ends the action. In addition, Presenter only has an application context, so it does not hold an activity context.

I'm not a Java expert, and this is my first foray into MVP, and using a static presenter made me uncomfortable. Am I missing something? My application is working fine and has become much more responsive.

View

public class MainActivity extends Activity{ private static Presenter presenter; protected void onResume() { if (presenter == null) presenter = new Presenter(this.getApplicationContext()); presenter.onSetView(this); presenter.onResume(); } protected void onPause() { presenter.onSetView(null); if(isFinishing())presenter.onPause(); } } 

Leading

 public class Presenter { private MainActivity view; Context context; public Model model; public Presenter(Context context) { this.context = context; model = new Model(context); } public void onSetView(MainActivity view) { this.view = view; } public void onResume(){ model.resume(); } public void onPause(){ model.pause(); } } 

Model

 public class Model { public Model(Context context){ this.context = context; } public void resume(){ //start data acquisition HandlerThreads } public void pause(){ //stop HandlerThreads } } 
+6
source share
3 answers

I would suggest two things.

  • Make Model , View and Presenter into interfaces.
    • Your MVP-View (a Activity , Fragment or View ) should be so simple that it does not need to be tested.
    • Your MVP-Presenter never interacts directly with Activity / Fragment / View, so it can be tested using JUnit. If you have dependencies on the Android Framework, this is bad for testing because you need to expose Android objects, use an emulator, or use a testing platform such as Roboelectric, which can be very slow.

As an example of interfaces:

 interface MVPView { void setText(String str); } interface MVPPresenter { void onButtonClicked(); void onBind(MVPView view); void onUnbind(); } 

The MVPPresenter class is now independent of the Android platform:

 class MyPresenter implements MVPPresenter{ MVPView view; @Override void bind(MVPView view){ this.view = view; } @Override void unbind() {this.view = null; } @Override void onButtonClicked(){ view.setText("Button is Clicked!"); } } 
  1. Instead of the static class Presenter , I would make it a saved fragment. Static objects must be carefully monitored and deleted for the GC manually when they are not needed (otherwise this is considered a memory leak). Using the preserved fragment, it is much easier to control the life time of the presenter. When the fragment to which the surviving fragment belongs ends, the surviving fragment is also destroyed, and the memory can be GC'd. See here for an example .
+6
source
  • Activity. Fragments should have only overridden methods of the View interface and other Android actions, fragments.
  • View has methods like navigateToHome, setError, showProgress, etc.
  • The host interacts with both View and Interactor (has methods such as onResume, onItemClicked, etc.)
  • Interactor has all the logic and calculations, performs such intensive tasks as db, network, etc.
  • Interactor isroid is free, can be tested using jUnit.
  • The action / fragment implements the view, creates an instance of the presenter.

Suggest changes for my understanding. :)

An example is always better than words, isn't it? https://github.com/antoniolg

+4
source

You're on the right track, and you're asking the right thing about static - whenever you notice that you have written this keyword, pause time and reflection.

The life of the leader should be tied directly to the "Activity" / "Fragment" section. Therefore, if the Activity is cleared by the GC, the same as the lead. This means that you should not contain a reference to the ApplicationContext in the presenter. It's nice to use ApplicationContext in Presenter, but it's important to break this link when destroying an Activity.

The presenter must also accept the View As Constructor option:

 public class MainActivity extends Activity implements GameView{ public void onCreate(){ presenter = new GamePresenter(this); } } 

and the lead looks like this:

 public class GamePresenter { private final GameView view; public GamePresenter(GameView view){ this.view = view; } } 

then you can notify the Presenter about activity life cycle events, for example:

 public void onCreate(){ presenter.start(); } public void onDestroy(){ presenter.stop(); } 

or in onResume/onPause - try to keep it symmetrical.

As a result, you have only 3 files:

(I take the code from another explanation I gave here , but the idea is the same.)

GamePresenter:

 public class GamePresenter { private final GameView view; public GamePresenter(GameView view){ this.view = view; NetworkController.addObserver(this);//listen for events coming from the other player for example. } public void start(){ applicationContext = GameApplication.getInstance(); } public void stop(){ applicationContext = null; } public void onSwipeRight(){ // blah blah do some logic etc etc view.moveRight(100); NetworkController.userMovedRight(); } public void onNetworkEvent(UserLeftGameEvent event){ // blah blah do some logic etc etc view.stopGame() } } 

I don’t know exactly why you want to use ApplicationContext instead of an Activity context, but if there is no special reason for this, you can change the void start() method to void start(Context context) and just use the Activity context. For me this will make more sense and also eliminate the need to create a singlet in your Application class.

Gameview

is an interface

 public interface GameView { void stopGame(); void moveRight(int pixels); } 

GameFragment is a class that extends Fragment and implements GameView and has GamePresenter as a member.

 public class GameFragment extends Fragment implements GameView { private GamePresenter presenter; @Override public void onCreate(Bundle savedInstanceState){ presenter = new GamePresenter(this); } } 

The key to this approach is a clear understanding of the role of each file.

The fragment controls everything related to viewing (buttons, TextView, etc.). It informs the presenter about user interaction.

The host is the engine, it takes information from the view (in this case it is a fragment, but note that this template is well suited for dependency injection). This is not a coincidence. He doesn’t know that View is a fragment - he doesn’t care) and combines it with the information that he receives from "below" (comms, database etc), and then commands View accordingly.

A view is simply the interface through which the facilitator interacts with the view. Note that methods read as commands are not as questions (e.g. getViewState ()) and not to report (e.g. onPlayerPositionUpdated ()) - commands (e.g. movePlayerHere (int position)).

+1
source

All Articles