Android中MVP的使用

以前在写项目的时候,没有过多考虑架构模式的问题,所以一开始使用的都是类似MVC的架构,严格来讲,之前的项目都不算MVC模式,只是简单的将网络请求与界面分离,然后通过Handler通知更新界面。随着项目越来越大,Activity和Fragment中代码越来越多,导致项目的维护变得越来越复杂,一开始重构成了MVC模式,随着深入了解,发现MVP模式能够大大减少Model和View层之间的耦合度,遂又将项目重构成MVP模式。最近又发现MVP已经开始不能满足需求,正在研究MVVM,后期会将MVVM的研究成果奉上。

当然不是所有的模块都使用MVP,MVP适合在一些逻辑复杂的模块中使用,如果业务逻辑简单,一个Activity就能搞定,也没有必要生搬硬套的做成MVP模式的。只要整体的模式思路使用MVP模式,其中夹杂一些MVC模式,甚至简单的模块不使用模块,不会对整个项目有太大的影响,反而是最优选择。

MVP模式也有其缺点,首先就是代码量多了,文件数量大大提升,但换来的好处是项目结构清晰,在前期只有原型图的时候,我们也可以提前开发,Model层负责数据的请求和数据的处理,写好Presenter层和View层后,基本不用改,只需关注Model层的变化就可以了;还是就是Activity内部类基本不再使用了,部分UI逻辑判断也可以转到Presenter去处理,View层(UI)也非常清晰干净。

什么是MVP

MVP是 模型(Model)、视图(View)、主持人(Presenter)的缩写,代表不同模块。

模型(Model):负责处理数据的加载或者存储;比如从网络或者本地数据库获取数据。

视图(View):负责界面数据的展示,与用户进行交互。

主持人(Presenter):相当于协调者,是Model和View层之间的桥梁,占据主导地位。

 

android mvp文件目录 安卓手机如何打开.mvp文件_数据

如图所示,Model层和View并不直接交互,而是通过Presenter层进行交互。Presenter持有Model层和View层的Interface的引用,而View层持有Presenter层Interface的引用。当View层需要展示数据的时候,会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据获取成功后会调用Presenter层的回调方法通知Presenter层数据加载完毕,然后Presenter层再调用View层的接口将获取到的数据展示给用户。

整个过程,View层告知Presenter层需要什么数据,Presenter层调用Model获取数据,Model数据获取完成告知Presenter层,Presenter层再调用View层将数据展示给用户。

这样分层减少了Model层和View层的耦合度:

1、可以使得Model层和View层单独开发与测试,互补依赖;

2、可以将Model层封装复用,减少代码量

3、方便构建单元测试,测试逻辑中存在的潜在BUG

4、清晰的结构,便于维护

准备工作

创建LoginBean类

public class LoginBean {
    private String mName;
    private String mPassWord;

    public String getmName(){
        return mName;
    }

    public void setmName(String name){
        mName = name;
    }

    public String getmPassWord(){
        return mPassWord;
    }

    public void setmPassWord(String passWord){
        mPassWord = passWord;
    }
}

模型层(Model)

1. 先写一个接口

public interface ILoginModel {
    void login(String username, String password, OnLoginListener loginListener);

    interface OnLoginListener{
        void LoginSuccess(LoginBean loginBean);
        void LoginFail();
    }
}

2. 继承接口进行数据请求

public class LoginModel implements ILoginModel {

    @Override
    public void login(String username, String password, OnLoginListener loginListener) {
        //模拟子线程耗时操作
        simulationRequest(username, password, loginListener);
    }

    private void simulationRequest(final String username,  final String password,final OnLoginListener loginListener) {
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(username.equals("Lking") && password.equals("123456")){//登录成功
                    LoginBean loginBean = new LoginBean();
                    loginBean.setmName(username);
                    loginBean.setmPassWord(password);
                    loginListener.LoginSuccess(loginBean);
                }else{//登录失败
                    loginListener.LoginFail();
                }
            }
        }.start();
    }
}

视图层(View)

1、先写一个接口

public interface ILoginView {
    /** 获取账号*/
    String getName();
    /** 获取密码*/
    String getPassword();
    /** 显示加载圈*/
    void showLoading();
    /** 关闭加载圈*/
    void hideLoading();
    /** 跳转到主界面*/
    void toMainActivity(LoginBean loginBean);
    /** 显示失败提示*/
    void showFailToast();
}

2、编写布局文件activity_mvp.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/color_ffffff"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_margin="10dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="账    号:"
            android:textColor="@color/color_000000"
            android:textSize="18sp" />

        <EditText
            android:id="@+id/lking_mvp_name_edt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:hint="请输入账号"
            android:textColor="@color/color_000000"
            android:textSize="16sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_margin="10dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="密    码:"
            android:textColor="@color/color_000000"
            android:textSize="18sp" />

        <EditText
            android:id="@+id/lking_mvp_pas_edt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:hint="请输入密码"
            android:textColor="@color/color_000000"
            android:textSize="16sp" />
    </LinearLayout>

    <TextView
        android:id="@+id/lking_mvp_login_txt"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:gravity="center"
        android:text="登录"
        android:textColor="@color/color_000000"
        android:textSize="18sp" />

    <ProgressBar
        android:id="@+id/lking_mvp_login_pro"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="gone"
        android:layout_marginTop="20dp" />
</LinearLayout>

3、继承接口进行数据显示

public class MvpActivity extends Activity implements ILoginView {
    private EditText mNameEdt;
    private EditText mPasswordEdt;
    private TextView mLoginBtn;
    private ProgressBar mLoading;
    private LoginPresenter mLoginPresenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp);

        initViews();
    }/**
     * 初始化控件
     */
    private void initViews() {
        mNameEdt = (EditText) findViewById(R.id.lking_mvp_name_edt);
        mPasswordEdt = (EditText) findViewById(R.id.lking_mvp_pas_edt);
        mLoginBtn = (TextView) findViewById(R.id.lking_mvp_login_txt);
        mLoading = (ProgressBar) findViewById(R.id.lking_mvp_login_pro);
        mLoginPresenter = new LoginPresenter(this);

        mLoginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mLoginPresenter.login();
            }
        });
    }

    @Override
    public String getName() {
        return mNameEdt.getText().toString().trim();
    }

    @Override
    public String getPassword() {
        return mPasswordEdt.getText().toString().trim();
    }

    @Override
    public void showLoading() {
        mLoading.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        mLoading.setVisibility(View.GONE);
    }

    @Override
    public void toMainActivity(LoginBean loginBean) {
        Toast.makeText(this, "登录成功,跳转到主界面", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showFailToast() {
        Toast.makeText(this, "登录失败,请重新输入账号和密码", Toast.LENGTH_SHORT).show();
        mNameEdt.setText("");
        mPasswordEdt.setText("");
    }

}

主持人(Presenter)

创建管理逻辑类

public class LoginPresenter {
    private ILoginView mILoginView;
    private LoginModel mLoginModel;
    private Handler mHandler = new Handler();

    public LoginPresenter(ILoginView iLoginView) {
        mILoginView = iLoginView;
        mLoginModel = new LoginModel();
    }

    public void login(){
        mILoginView.showLoading();
        mLoginModel.login(mILoginView.getName(), mILoginView.getPassword(), new ILoginModel.OnLoginListener() {
            @Override
            public void LoginSuccess(final LoginBean loginBean) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mILoginView.hideLoading();
                        mILoginView.toMainActivity(loginBean);
                    }
                });

            }

            @Override
            public void LoginFail() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mILoginView.hideLoading();
                        mILoginView.showFailToast();
                    }
                });

            }
        });
    }
}