When I started my journey as an Android developer, the words “Mobile Application Architecture” caused me deep bewilderment, Google and articles on Habré drove me into even greater depression - I look at the book and see nothing. I think if you are reading this article, you have already studied this picture more than once and tried to understand what is happening:
The problem of understanding the architectural approach in mobile development, in my opinion, lies in the abstractness of the architecture itself. Each developer has his own vision of how to correctly implement this or that pattern. More or less decent examples of MVP implementation were found in the English-speaking sector of the Internet, which is not surprising. Let's briefly look at what is what and move on to an example. Model - data level. I don’t like to use the term “business logic”, so in my applications I call it Repository and it communicates with the database and the network. View — display level. It will be Activity , Fragment or Custom View if you don’t like dancing with a tambourine and interacting with the life cycle. Let me remind you that initially all Android applications are subordinated to the MVC structure , where the Controller is an Activity or Fragment . Presenter is a layer between View and Model. View transmits the events that occur to it, the presenter processes them, if necessary, accesses the Model and returns data to the View for rendering. In relation to Android and a specific example, I will highlight the important part - Contract. This is the interface that describes all the interactions between the above components. To summarize the theoretical part:
- View knows about Presenter;
- Presenter knows about View and Model (Repository);
- Model by itself;
- Contract governs the interactions between them.
MainContract
:
public interface MainContract {
interface View {
void showText();
}
interface Presenter {
void onButtonWasClicked();
void onDestroy();
}
interface Repository {
String loadMessage();
}
}
For now, we are simply highlighting the 3 components of our future application and what they will do. Next we will describe the Repository:
public class MainRepository implements MainContract.Repository {
private static final String TAG = "MainRepository";
@Override
public String loadMessage() {
Log.d(TAG, "loadMessage()");
/** Здесь обращаемся к БД or сети.
* Я специально ничего не пишу, чтобы не загромождать пример
* DBHelper'ами и прочими не относяшимеся к теме an objectми.
* Поэтому я буду возвращать строку Сосисочная =)
*/
return "Сосисочная у Лёхи»;
}
}
Everything is clear with it, just loading and unloading data. Next up is Presenter:
public class MainPresenter implements MainContract.Presenter {
private static final String TAG = "MainPresenter";
//Компоненты MVP applications
private MainContract.View mView;
private MainContract.Repository mRepository;
//Сообщение
private String message;
//Обрати внимание на аргументы конструктора - мы передаем экземпляр View, а Repository просто создаём конструктором.
public MainPresenter(MainContract.View mView) {
this.mView = mView;
this.mRepository = new MainRepository();
Log.d(TAG, "Constructor");
}
//View сообщает, что кнопка была нажата
@Override
public void onButtonWasClicked() {
message = mRepository.loadMessage();
mView.showText(message);
Log.d(TAG, "onButtonWasClicked()");
}
@Override
public void onDestroy() {
/**
* Если бы мы работали например с RxJava, в этом классе стоило бы отписываться от подписок
* Кроме того, при работе с другими методами асинхронного андроида,здесь мы боремся с утечкой контекста
*/
Log.d(TAG, "onDestroy()");
}
}
Do you remember I wrote about dancing with a tambourine and the life cycle? The Presenter lives as long as its View lives, when developing complex user scenarios, I advise you to duplicate all View callbacks in the Presenter and call them at the appropriate moments, duplicating the Activity/Fragment lifecycle, in order to understand in time what needs to be done with the data that is hanging currently in the “interlayer”. And finally, View:
public class MainActivity extends AppCompatActivity implements MainContract.View {
private static final String TAG = "MainActivity";
private MainContract.Presenter mPresenter;
private Button mButton;
private TextView myTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Создаём Presenter и в аргументе передаём ему this - эта Activity расширяет интерфейс MainContract.View
mPresenter = new MainPresenter(this);
myTv = (TextView) findViewById(R.id.text_view);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.onButtonWasClicked();
}
});
Log.d(TAG, "onCreate()");
}
@Override
public void showText(String message) {
myTv.setText(message);
Log.d(TAG, "showMessage()");
}
//Вызываем у Presenter метод onDestroy, чтобы избежать утечек контекста и прочих неприятностей.
@Override
public void onDestroy() {
super.onDestroy();
mPresenter.onDestroy();
Log.d(TAG, "onDestroy()");
}
}
What's going on?
- Activity, also known as View,
onCreate()
creates a Presenter instance in a method and passes itself to its constructor. - When a Presenter is created, it explicitly receives a View and creates a Repository instance (by the way, it can be made a Singleton)
- When a button is pressed, the View knocks on the presenter and says: “The button was pressed.”
- Presenter turns to Repository: “Download this crap for me.”
- The Repository loads and delivers the “stuff” to the Presenter.
- Presenter turns to View: “Here is the data for you, draw it”
GO TO FULL VERSION