JavaRush /Java Blog /Random EN /MVP in Android for the little ones

MVP in Android for the little ones

Published in the Random EN group
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: MVP in Android for the little ones - 1The 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.
Actually, the example itself, for the simplicity of the experiment, by clicking on a button, we will load a row from the database and display it in a TextView . For example, the database contains a list of the best restaurants in the city. Let's start with the contract: Let's create an interface 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”
That's it, guys. PS It is important to clearly delineate responsibilities between components. For example, in one of my training projects, when clicking a button, it was necessary to change data in the database. The model was described by a POJO class, I passed information about the location of the view, which is responsible for information about the object on the screen, Presenter looked for this object in the list and sent it to be written to the Repository. Does everything seem logical? But my mentor pointed out the following: the Repository should ONLY do writing and reading, it should not pull the necessary information out of the POJO and decide what it needs to write. The Presenter must give him only the information to record and nothing more. There are no strict framework for implementing architecture: experiment, try, look for what is convenient for you personally. And don’t forget to show your senior comrades on code review =) The example is available on GitHub: https://github.com/admitrevskiy/MVP_Example
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION