Android MVP模式学习


MVP简介

       随着时代的发展,移动端UI越来越丰富、业务越来越复杂,用户对界面的美观、渲染的速度、数据读取的速度要求也越来越高。为了在保证拥有酷炫的UI、复杂的业务逻辑的条件下给用户更好的体验,需要对view、model、controller职责进行细分,让view专注于处理数据的可视化以及和用户的交互、让model专注于数据的处理。一种新的模式MVP(Model - View - Presenter)应运而生。

MVP和MVC的差异

       MVP模式是从MVC模式理念的基础上衍生出来的,在MVC的基础上对V和M进行了绝对的解耦,两者之间只有通过Presenter才能通信,优化了V层和C层,职责划分更明确、单一。MVC中,M层和V层是可以通信的,MVP中M层和V层是完全隔离的,只能通过P层来通信,如图(这里借图一用)。


      在MVC模式时期,由于UI越来越丰富、逻辑处理越来越复杂,Activity中对UI的活动展示以及和用户的交互也越来越复杂,代码量比较大,同时受限于Android的线程安全以及很多时候需要使用到Activity的生命周期,我们有不少的Controller层的代码也会写在Activity里面,也就是Activity同时承担了View层和部分Controller层的工作,影响开发效率,不利于维护和扩展。
       于是,我们将复杂的逻辑移至另外一个类Presenter,让Activity只负责UI的展示以及和用户的交互。View层,创建View Interface,定义好View层的活动事件,Activity加载布局、实现View Interface,将View Interface传入Presenter实例,通过View Innterface和Presenter实现交互;Model层,负责存储、检索、操纵数据,可以通过创建一个Model Interface来进一步实现解耦;Presenter层,作为View和Model交互的中间枢纽,负责接收用户操作,调用Model层,将数据反馈到View层展示给用户。

选择MVP的必要性

       1)降低了耦合性;
       2)各模块职责划分明确,利于协同开发;
       3)业务逻辑独立出来,通过接口调用,方便单元测试;
       4)代码重用性高;

示例

       这里跟随很多大牛的脚步,同样使用登录页面来作为第一次体验MVP的示例。
       ①登录页面布局activity_main.xml代码,布局比较简单,两个输入框,一个登录按钮,一个加载等待条(默认隐藏)。

<?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
阅读原文


猜你喜欢

转载自blog.csdn.net/liujibin1836591303/article/details/80092311
今日推荐