What is MVP
MVP is a way of sharing responsibility in the application code.
Model provides data for
Presenter .
View performs two functions: it responds to commands from the user (or from UI elements), sending these events to the
Presenter and modifies the gui on demand of the
Presenter .
Presenter acts as a link between
View and
Model .
Presenter receives events from
View , processes them (using or not using the
Model ), and commands the
View about how it should change itself.
This approach to the division of responsibility has a number of advantages:
- Writing tests to the code is greatly simplified.
- Easy to change a part without breaking another
- The code is broken into small pieces, due to which it becomes more understandable and readable
At the same time, of course, there are also disadvantages:
- Code gets bigger
- You need to get used to this approach.
- At the moment, not very common ( but well-known ) approach, so you have to tell everyone about it
MVP in Android
Activity in Android is
God object . It usually has the following responsibility:
- Full GUI Management
- Handling user interaction.
- Run asynchronous tasks.
- Processing the result of the asynchronous task.
The saddest thing is that our God Object is not immortal - Activity also dies when the configuration changes.
MVP removes some of the responsibility for the Activity. All work with asynchronous tasks goes to
Presenter . All business logic is in
Presenter and
Model . Activity, in turn, becomes
View . She starts to simply display what
Presenter says and sends events to
Presenter so that he can decide what to do next.
')
Before writing our solution, we studied many articles and implementations of the
MVP concept in Android (see links at the end of the article). Based on the analysis, a list of solution requirements has been established:
- View should bind to existing Presenter when changing configuration
- After binding the View to an existing Presenter , the View should display the current state of the Presenter.
- Presenter should be able to ( if necessary ) live no matter who subscribes to it or unsubscribes from it.
At the moment, none of the existing solutions can do all these points at the same time. As it seemed to us at first, the
Mosby library was the most suitable for us. But later it turned out that when using it, we would have to write too much code every time. Especially, for the implementation of the first two points from our list of requirements. Therefore, it was decided to develop their own decision.
Moxy - theory
Our solution is very different from all the others (even the
MVP concept itself was modernized) in that
ViewState was mixed up between
View and
Presenter . And it is absolutely necessary there. He is responsible for ensuring that each
View always looks exactly as the
Presenter wants.
ViewState stores a list of commands that were transferred from
Presenter to
View . And when the “new”
View joins the
Presenter , the
ViewState automatically applies to it all the commands that the
Presenter issued earlier. Thus, it turns out that no matter what happens with
View due to the fault of Android,
View will remain in the correct state anyway. To do this, you will only need to get used to changing the
View solely by the commands from the
Presenter . Note that this is one of the basic rules of
MVP and applies not only to Moxy.
A schematic illustration of how this works:

What happens on this scheme:
- In the View event occurs
that is passed to the presenter - Presenter passes command
in viewstate - Presenter starts an asynchronous request
in Model - ViewState adds command
in the queue of commands, and then passes it to the View - View brings itself to the state specified in the command.

- Presenter gets query result
from model - Presenter passes two commands to ViewState
and 
- ViewState saves commands
and
to the command queue and send them to the View - View brings itself to the state specified in the commands.
and 
- New / recreated View joins an existing Presenter
- ViewState transfers the saved command list to a new / recreated View.
- New / recreated View brings itself to the state specified in the commands
,
and 
Moxy - features
Moxy has several significant advantages over other solutions:
- Presenter does not re-create when recreating an Activity (this simplifies working with multithreading at times)
- Automate the full recovery of what the user sees when recreating an Activity (including when dynamically adding Android View elements)
- Ability to change several Views from one Presenter at once (in practice it turned out to be extremely convenient)
To do this, Moxy has several mechanisms that can be combined with each other as you please. The most powerful mechanisms are annotations, on the basis of which the code is generated. And during the execution of the program, the tool called
MvpDelegate
begins to fully use the generated code.
The following annotations are available:
- @InjectPresenter - abstract for the management of the life cycle Presenter
- @InjectViewState - an annotation for binding ViewState to Presenter
- @StateStrategyType - summary for managing the strategy of adding and removing commands from the command queue in ViewState
- @GenerateViewState - annotation for generating ViewState code for a specific View interface
All this further.
Moxy - MvpPresenter
Each application contains some business logic. In the
MVP concept, all business logic is located in the
Presenter and in the
Model . In fact, this means that you practically do not program in
View . In order for your
Presenter not to become a God Object, you need to separate each separate block of business logic into a separate
Presenter . In this case, you will get a lot of
Presenter , but they will be very simple and understandable. For example, if you had two business logic on one screen, and then they split into 2 different screens, then you simply change the View. A
Presenter what they were, and will remain so. Also, in this case, you can easily reuse one
Presenter in several places (for example, BasketPresenter, pass-through through the entire application). It also simplifies code testing - you just check a small
Presenter that it does everything right.
For
Presenter in Moxy the class
MvpPresenter<View extends MvpView>
.
MvpPresenter
contains an instance of
ViewState , which at the same time must implement the same type of
View
that came in
MvpPresenter
. This
ViewState instance can be accessed from the
public View getViewState()
method. And during development you do not think that you are working with
ViewState , but simply give commands for
View through this method how to change it. There are also methods for attaching / decoupling
View from
Presenter (
public void attachView(View view)
and
public void detachView(View view)
). Note that multiple
views can be
associated with one
Presenter . They will always be up to date (at the expense of
ViewState ). And if you want the
View binding / decoupling to pass not through the standard
ViewState field, you can redefine these methods and work with the incoming
View as you wish.
For example, you might want to use a non-standard ViewState , which does not implement the View
interface, if you need to.In the class
MvpPresenter
is also an interesting method
protected void onFirstViewAttach()
. It is very important to understand when this method will be called and why it is needed. This method is called when any
View is bound to a specific
Presenter instance for the first time. And when
another View is attached to this
Presenter , the state from
ViewState will be applied to it. And here it does not matter, this
new View is a completely different
View , or recreated as a result of a configuration change. This method is suitable, for example, to load a news list when you first open the news list screen.
At the moment when the command came to
View , you may need to understand, is this a new command, or is it a command to restore the state? For example, if this is a fresh command, then you need to apply a command with animation. Otherwise, do not apply animation. This can be done through different StateStrategy, or through complex flags in the
Bundle savedState
. But the correct solution would be to use the
Presenter (or
ViewState ) method.
public boolean isInRestoreState(View view)
, which tells you what state the particular
View is in . This way you can understand whether you need animation or not.
Moxy - MvpView
and MvpViewState
The simplest component of
MVP is
View . You need to create an interface that is inherited from the
MvpView
interface marker and describe in it methods that the
MvpView
will be able to perform. In addition to
View , our library has a
ViewState entity, which is directly related to
View .
ViewState is a descendant of
MvpViewState<View extends MvpView>
. It manages one, or several,
View (all of one type
View
). And every time a team from the
Presenter comes to
ViewState ,
ViewState sends it to all the
View he knows about. Also,
MvpViewState
has a
protected abstract void restoreState(View view)
method,
protected abstract void restoreState(View view)
, which will be called when any
View is recreated, or when a new
View is bound to the
Presenter to the
ViewState . After this method is completed, the “new”
View will take the desired state.
It is worth noting that
MvpViewState
stores a list of all
View attached to it. And it will be good if you do not forget to untie the
View that has already been destroyed. But if you suddenly forget to do this, do not worry much -
MvpViewState
does not store direct links to the
View , but the
WeakReference
, which will still help the GC. And if you use a mechanism such as
MvpDelegate , then you can not worry about it - it binds the
View to the
Presenter and untie them.
Moxy - @GenerateViewState and @InjectViewState
Since
ViewState in most cases is a rather monotonous layer between
View and
Presenter , a code generator was written that does all the dirty work for you. By applying the
@GenerateViewState annotation to your View interface, you will get the generated
ViewState class. And so that you don’t have to independently search and create an instance of this class in
Presenter , there is the @InjectViewState annotation. Simply apply it to your
Presenter class. Further
MvpPresenter
will do everything itself - it will create an instance of this
ViewState , fold it to itself as a field and use it everywhere. You just need to work with the
public View getViewState()
MvpPresenter
from
MvpPresenter
.
In case you do not want to use
@GenerateViewState , but your
ViewState implements the
View interface, you can still use the @InjectViewState annotation. In this case, pass to this annotation, as a parameter, the class of your
ViewState .
Be careful when applying the @InjectViewState annotation to a typed Presenter.For example, if you have this code:
@InjectViewState public class MyPresenter<T extends MvpView> extends MvpPresenter<T> {
The Annotation processor will not correctly understand the
View class, whose
ViewState should be used. In this case, you can explicitly pass the
View class to the view parameter of the @InjectViewState annotation.
Also note that an interface annotated with @GenerateViewState must be non-typed.The catch lies in the fact that when generating code, we need to know the types of all parameters of all methods. Otherwise, the resulting code will not work.
Therefore,
you can write such code:
@GenerateViewState public interface ConcreteInterface extends AbstractInterface<String> {
You
cannot write such code:
@GenerateViewState public interface ConcreteInterface<Type> extends AbstractInterface<Type> {
Moxy - StateStrategy
for commands in ViewState
By default, all commands for the
View are saved in
ViewState simply in the order in which they arrived there. And after the teams have been applied, they continue to lie in this queue. But this behavior can be changed by applying the @StateStrategyType annotation to the
View interface and to its methods. At the input, this annotation receives a parameter in which you must specify the
StateStrategy
class that you want to use. If you apply this annotation to the entire
View interface, then those methods for which a strategy is not specified will use this strategy.
StateStrategy manages the command queue through two methods:
void beforeApply
and
void afterApply
. The first method will be called before the command is sent to the
View (the
beforeApply
method will be called as soon as a command from the
Presenter arrives). At this point, in the default strategy, the command is added to the queue. The second
afterApply
method will be called each time the command is applied to the
View . Both in the first and in the second method you can change the list of commands as you like.
Let's look at strategies that are already implemented in Moxy:
- AddToEndStrategy - adds the incoming command to the end of the queue. Used by default
- AddToEndSingleStrategy - adds the incoming command to the end of the command queue. Moreover, if a team of this type is already in the queue, the existing one will be deleted.
- SingleStateStrategy - clear the entire queue of commands, then add itself to it
- SkipStrategy - the command will not be added to the queue, and will not change the queue
If you have some specific logic and you lack these strategies, then you can make your own strategy. In this case, the method of tagging methods will help you. You can pass the tag parameter to the @StateStrategyType annotation (the default is the name of the method). Then, using this tag, you can see in the
void beforeApply(List<ViewCommand<View>> currentState, ViewCommand<View> incomingCommand)
and
void afterApply(List<ViewCommand<View>> currentState, ViewCommand<View> incomingCommand)
that for
ViewComand
came to you (from the
ViewCommand
String getTag()
method).
Before writing your strategies, look at the code already implemented - maybe it will be useful to you.
Moxy - MvpDelegate
and the life cycle of MvpPresenter
By itself, the
Presenter is not created anywhere, is not stored anywhere, and is not available anywhere. And so that you do not have to invent anything to solve these problems, we made a mechanism such as
MvpDelegate
. He ensures that, where his copy is, all
Presenters are correctly initialized. For this, all you have to do is to transfer to it all the main points of the life cycle of your
View . You can see which methods to invoke when in the
MvpActivity
or
MvpFragment
.
In order for
MvpDelegate
find all the
Presenters , you must annotate them with @InjectPresenter. This annotation is very powerful. Through it, you can control how long the
Presenter will live. If you want the
Presenter to live for as long as there is a
View in which it is contained (+ while the configuration is changing), simply add this annotation to the
Presenter field. In case you want
Presenter to live no matter who subscribes to it, you will need to do two things. The first is to inform
MvpDelegate
that the
Presenter is not tied to the life cycle of the one who requested it. For this, you need to set the value of the type parameter of the annotation @InjectPresenter as PresenterType.GLOBAL. The second is that you must pass information to
MvpDelegate
on which he can find the
Presenter you need in the repository of all
Presenter . There are two options for how to do this:
First option. In the @InjectPresenter annotation you set the value for the tag parameter. Then
MvpDelegate will try to find the global
Presenter with the tag. If he finds it, then simply install it in this field. Otherwise, it will create a suitable
Presenter , add it to the repository, and install it in this field. Taking into account the fact that several
Views can be linked to one
Presenter , this mechanism opens up many possibilities in front of you.
The second option (for the parzimerizirovannogo tag). In fact, it is similar to the first option. The only difference is that in the second case you cannot know in advance which
Presenter tag will be. Those. tag must be generated dynamically. Then you have to try a little:
- Create your own
PresenterFactory
implementation - In the @InjectPresenter annotation, set the parameters:
- In factory, set your
PresenterFactory
class - In presenterId, set the Presenter string identifier (this is necessary to distinguish Presenter classes with the same factories in the same class)
- Get your own interface containing one and only one method that will return a parameter for the desired type of factory:
- Annotate this interface as @ParamsProvider (PresenterFactoryClass), passing annotations, as a parameter, to the class of your
PresenterFactory
- Describe the method that will return the parameter, should receive one
String
parameter as input (this parameter will contain the same presenterId parameter from the @InjectPresenter annotation)
- The object that contains a Presenter , in the annotation @InjectPresenter of which this
PresenterFactory
is indicated, must implement the one created in par. 3. interface
Here you should know that you did not think that this place is too confusing. So it is, it is confused. Just know that if you need this functionality, follow this short list of rules, and you will understand everything and you will succeed.
In addition to the above functionality,
MvpDelegate
can be a parent / child delegate for another. This is necessary so that you can automate the
Presenter life cycle not only within the Activity / Fragment, but also within other elements that do not have an independent life cycle (for example, in the adapter or even in the
ViewHolder
of the adapter element). If you set one
MvpDelegate
as the parent of another
MvpDelegate
, then the child delegate will receive all the events of the life cycle of the parent delegate. To do this, simply call the
public void setParentDelegate(MvpDelegate delegate, String childId)
method on the target
MvpDelegate
. As a
delegate
he expects to receive the parent
MvpDelegate
. As
childId
, you must specify a unique identifier by which the local
Presenter of one delegate-child will differ from the local
Presenter of another delegate-child.
Note that if the
MvpDelegate
has already been called on the
onCreate
. then you need to call the
onCreate
method on the
onCreate
delegate
onCreate
. Why is it important? To understand this, let's see how
MvpDelegate
works.
MvpDelegate
in addition to controlling the initialization of the
Presenter fields, does another very important thing. He binds and untips
View from
Presenter . Binding the
View to the
Presenter occurs in the
onStart
method, and
onDestroy
in the
onDestroy
method.
Fragment
little different, see github .Why in these methods?After calling onCreate
MvpDelegate
, all fields marked with the @InjectPresenter annotation are ready to work. But View is not yet attached to them. The view will be bound to the Presenter after the MvpDelegate
method MvpDelegate
void onStart()
.
After that, the Presenter can interact with the View (and then, if the Presenter was first attached to this View , the Presenter void onFirstViewAttached()
method will be called). After calling void onDestroy()
on MvpDelegate
, the View will be decoupled from the Presenter . And then there are two questions. First, why is View not bound to Presenter in onCreate
, but in onStart
? Secondly, once the binding has happened in onStart
, then why is the onStop
not in onStop
, but in onDestroy
? Quite reasonable questions. And the answer to them is that it is a) more convenient, and b) easier. More conveniently, ViewState is applied to the View as soon as the View has been bound to the Presenter . And if you bind the View to the Presenter in onCreate
, it turns out that you will need to call the onCreate
delegate method yourself in the Activity after you complete all the Android View initialization onCreate
Activity. It is not comfortable. It is convenient to simply make one Activity from which all the activities of your application will inherit, and in the onCreate
method of this Activity simply execute the onCreate
delegate onCreate
. And taking into account that the view is bound in onStart
, there will be no problems. Secondly, if you unbind the View in onStop
, then the binding will definitely happen every time onStart
(now the binding of the View occurs in onStart
, only if onCreate
was done onCreate
). This means that ViewState will be restored with every onStart
. This means that the entire state will be rolled back anew, even if the Activity View was not destroyed, but simply became invisible for a while. Therefore, decoupling the View from the Presenter occurs in onDestroy
. Note: onDestroy
will not be called if Android decides to kill the Activity process, but in this case the Presenter will be destroyed.
MvpDelegate
uses special storage for the
Presenter . Access to this repository is obtained through
MvpFacade
.
MvpFacade
- contains the
Presenter storage and some other elements designed to help
MvpDelegate to do its job optimally.
Although MvpFacade is a singleton, it would be great if you run its
public static void init()
method, for example, in the Application's
onCreate()
method. Or you can inherit your Application from
MvpApplication
supplied in Moxy. Then at the moment when
MvpDelegate
turns to this singleton, it will be ready for work.
Moxy - Model
An important element of
MVP is
Model . But in Moxy, this part of the
MVP is in no way affected. The thing is that this makes no sense. Each project has its own requirements for
Model . In some
places, Model is just a set of classes for working with an API and the work itself with an API (for example, via Retrofit). Somewhere in the
Model also includes additional business logic. In some projects, the use of the Clean Architecture approach is relevant. In this case, additional entities appear inside the
Model , for example,
Interactor and
Repository . And given the fact that
Presenter is completely untied from the Activity life cycle, you can safely create an instance of a specific
Model inside the
Presenter and work with it. Using DI you can connect the desired
Model in
Presenter . And in the future, using the same DI, quietly replace
Model for tests.
In any case, it is extremely convenient to use Rx to work with the
Model . Then you can make
Model's public methods return
Observable
. In this case, it will be easy to make the interaction of
Model ⇒
Presenter , and at the same time
Model ⇔
Model . This will make it possible to easily make parallel execution of queries from
Presenter to
Model .
Moxy - total
As a result, we have a library that solves all the problems of the life cycle. You will always show the user exactly the state that is relevant to him, and at the same time, you will not have to do anything extra. Just describe all the commands for
View by separate methods. And avoid changing the
View from the
View itself. If you showed a dialogue with a team from
Presenter , then when closing the dialogue, there should be a command from
Presenter . Otherwise,
ViewState will show you the dialog again after changing the configuration.
I would like to note that the library does not limit you in any way in choosing the implementation of multithreading in your application. You can use Rx, AsyncTask, Thread, Executor. The main thing is to be careful, work with
View only from the main stream. Also, Moxy will not solve problems with
commit()
fragments after running
onSaveInstanceState()
. Therefore, remember to close the transaction using
commitAllowingStateLoss()
. Also, it will not solve problems with memory leaks - if you pass the link to Context / Activity / Fragment to
Presenter (and then to
ViewState ), then the memory may leak.
Be careful.Useful materials
Moxy would not have turned out the way it turned out if it were not for the numerous works of other people. Here are some of them:
- Android Application Architecture (Android Dev Summit 2015)
- Android Testing Codelab - MVP theme is not affected much, but you can learn something for yourself. You can also see how to test MVP .
- Nucleus is an example of MVP implementation , with a zamashkoy on the processing of the life cycle.
- Mosby is the best MVP implementation before the Moxy release. The principles of MVP , which are critical to understand, are perfectly written .
- Old Mosby - a guide to the first version of Mosby. Extremely useful for understanding what MVP is .
- STINSON'S PLAYBOOK FOR MOSBY – , MVP . , , .
- Android Reactive MVP: – , Android-, MVP
- Andrtoid Clean Architecture – , , .
- . Speaker Clean Architecture MVP – Clean Architecture, .
- Mosby issues 85 – , Repository.
Moxy –
To connect Moxy to your project, just add it in dependencies. Moxy consists of three parts. One of them is responsible for providing you with a Moxy SDK. It's pretty easy to connect: dependencies{ ... compile 'com.arello-mobile:moxy:1.1.1' }
If you want to have access to such auxiliary classes as MvpApplication
, MvpActivity
and MvpFragment
, also connect moxy-android
: dependencies{ ... compile 'com.arello-mobile:moxy-android:1.1.1' }
The other part is responsible for handling annotations and generating code. And here you need to decide.If you do not have any special requirements, your project is a normal Android project, and you do not want the generated code to be available from your when, then connect the dependency like this: dependencies{ ... provided 'com.arello-mobile:moxy-compiler:1.1.1' }
If you want to have direct access to the generated code, then you should use android-apt :- Modify your project's build.gradle:
buildscript { dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } }
- Modify your application's build.gradle:
apply plugin: 'com.neenbedankt.android-apt' dependencies { ... apt 'com.arello-mobile:moxy-compiler:1.1.1' }
Library sources can be found on Github: https://github.com/Arello-Mobile/MoxyA full example application using Moxy: https://github.com/Arello-Mobile/Moxy/tree/master/sample-githubAt the moment when we compile a representative list of questions on our library, on how to use it, on MVP as a whole, a separate article will be made, which will highlight the most popular / interesting questions. Questions can be asked here in the comments, write to me (@senneco) and another author of the library - Xanderblinov . Or you can contact the entire Android development department at Arello Mobile by writing to java-developers@arello-mobile.com .From the authors of the Moxy senneco libraryand Xanderblinov