Android MVP pattern learning


Introduction to MVP

       With the development of the times, the mobile terminal UI is becoming more and more abundant, and the business is becoming more and more complex. Users have higher and higher requirements for the beauty of the interface, the speed of rendering, and the speed of data reading. In order to give users a better experience under the condition of ensuring cool UI and complex business logic, it is necessary to subdivide the responsibilities of view, model, and controller, so that view can focus on processing data visualization and interaction with users. The model focuses on data processing. A new pattern MVP (Model - View - Presenter) came into being.

Differences between MVP and MVC

       The MVP model is derived from the concept of the MVC model. On the basis of MVC, V and M are absolutely decoupled. The communication between the two can only be done through the Presenter. The V layer and the C layer are optimized, and the division of responsibilities More specific and single. In MVC, the M layer and V layer can communicate. In MVP, the M layer and V layer are completely isolated and can only communicate through the P layer, as shown in the figure (here is borrowed from the picture).


      In the period of MVC mode, due to the richer UI and more complex logic processing, the display of UI activities in Activity and the interaction with users are also more and more complicated, the code volume is relatively large, and it is limited by the thread safety of Android. And many times we need to use the life cycle of the Activity. We have a lot of Controller layer codes written in the Activity, that is, the Activity undertakes the work of the View layer and part of the Controller layer at the same time, which affects development efficiency and is not conducive to maintenance and expand.
       Therefore, we moved the complex logic to another class Presenter, so that the Activity is only responsible for the display of the UI and the interaction with the user. View layer, create View Interface, define the activity events of View layer, Activity loads the layout, implements View Interface, passes View Interface to Presenter instance, realizes interaction through View Interface and Presenter; Model layer is responsible for storing, retrieving and manipulating data, Decoupling can be further achieved by creating a Model Interface; the Presenter layer, as the intermediate hub for the interaction between the View and the Model, is responsible for receiving user operations, calling the Model layer, and feeding back data to the View layer for display to the user.

The necessity of choosing an MVP

       1) The coupling is reduced;
       2) The responsibilities of each module are clearly divided, which is conducive to collaborative development;
       3) The business logic is independent and called through the interface, which is convenient for unit testing;
       4) High code reusability;

example

       Following in the footsteps of many big cows here, the login page is also used as an example of experiencing MVP for the first time.
       ①Login page layout activity_main.xml code, the layout is relatively simple, two input boxes, a login button, and a loading waiting bar (hidden by default).

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginActivity"
    android:padding="20dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_centerVertical="true"
        >

        <EditText
            android:id="@+id/userName"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:hint="用户名"/>

        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_marginTop="5dp"
            android:hint="密码"/>
        
        <Button
            android:id="@+id/loginBtn"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_marginTop="10dp"
            android:text="Login"/>

    </LinearLayout>

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:visibility="gone"/>

</RelativeLayout>

       ②View Interface,这里定义的是View的活动,这里登录页面存在的活动有(显示加载等待框、隐藏加载等待框、返回登录成功或者登录失败),所以ILoginView代码如下。

public interface ILoginView {
    /*显示等待框*/
    void showProgress();
    /*隐藏等待框*/
    void hideProgress();
    /*登录成功*/
    void loginSuccess();
    /*登录失败*/
    void loginError();
}

       ③Model Interface,需要在model中去判断用户名和密码是否正确,并返回处理结果,所以ILoginModel需要定义一个checkLogin方法,再定义一个回调的内部接口,因为只有P能主动通信M,M不能主动通信P,所有这里采用回调来返回结果。

public interface ILoginModel {

    /*回调接口*/
    interface OnLoginListener{
        /*登录成功回调*/
        void loginSuccess();
        /*登录失败回调*/
        void loginError();
    }

    /*判断输入的用户名、密码,调用内部接口回调*/
    void checkLogin(String userName,String password,OnLoginListener listener);

}

       ④实现ILoginModel,这里虚拟登录。

public class LoginModelImpl implements ILoginModel {
    @Override
    public void checkLogin(final String userName, final String password, 
final OnLoginListener listener) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if(userName.equals("1") && password.equals("1")){
                    listener.loginSuccess();
                }else{
                    listener.loginError();
                }
            }
        },3000);
    }
}

       ⑤Presenter Interface,Presenter接收用户点击按钮的事件,然后调用Model处理数据,ILoginPresenter需要toLogin方法。

public interface ILoginPresenter {

    void toLogin(String userName,String password);

}

       ⑥实现Presenter,P层作为V和M的交互媒介,需要同时持有V和M,在M处理完返回的时候还需要通知到V层,所以还要实现M层的回调接口。由于V层可以主动沟通P层,所以V层和P层互相持有,这里V层直接在构造函数里传递过来。

public class LoginPresenterImpl implements ILoginPresenter,
                                    ILoginModel.OnLoginListener {

    private ILoginView mView;
    private ILoginModel mModel;

    public LoginPresenterImpl(ILoginView iLoginView) {
        mView = iLoginView;
        mModel = new LoginModelImpl();
    }

    @Override
    public void toLogin(String userName, String password) {
        mView.showProgress();
        mModel.checkLogin(userName,password,this);
    }

    @Override
    public void loginSuccess() {
        mView.hideProgress();
        mView.loginSuccess();
    }

    @Override
    public void loginError() {
        mView.hideProgress();
        mView.loginError();
    }
}

       ⑦View实现加载,这里在Acitvity中操作,实现View Interface,实例化Presenter,将View Interface作为参数传递给Presenter。

package liu.wolf.firstmvpdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

import liu.wolf.firstmvpdemo.presenter.LoginPresenterImpl;
import liu.wolf.firstmvpdemo.view.ILoginView;

public class LoginActivity extends AppCompatActivity implements 
                                                ILoginView{

    private ProgressBar progressBar;
    private EditText userName,password;
    private Button loginBtn;

    private LoginPresenterImpl mPresenter = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mPresenter = new LoginPresenterImpl(this);

        progressBar = findViewById(R.id.progressBar);
        userName = findViewById(R.id.userName);
        password = findViewById(R.id.password);
        loginBtn = findViewById(R.id.loginBtn);

        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.toLogin(userName.getText().toString(),
                        password.getText().toString());
            }
        });
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.GONE);
    }

    @Override
    public void loginSuccess() {
        Toast.makeText(LoginActivity.this,"登录成功!",
                Toast.LENGTH_LONG).show();
    }

    @Override
    public void loginError() {
        Toast.makeText(LoginActivity.this,"登录失败!",
                Toast.LENGTH_LONG).show();
    }
}

代码下载:https://download.csdn.net/download/liujibin1836591303/10374061
阅读原文


Guess you like

Origin blog.csdn.net/liujibin1836591303/article/details/80092311