Android中MVP框架理解

转载于:Android中MVP框架理解 - AND_Devil - CSDN博客

概述

对于MVP,本人有问题:

为何这个模式出来后,就能被广大的Android的程序员接受呢?

关于MVC

  • 关于MVC
  • MVC 全称是Model - View - Controller,是模型(model)-视图(view)-控制器(controller)的缩写。
  • Model:业务逻辑和数据处理(数据库存储操作,网络数据请求,算法,耗时操作…),专为存储和管理应用数据而生。
  • View:对应于布局文件(凡是能够在屏幕上看见的对象,都是视图对象)。
  • Controller:用户交互(读取View视图,向Moder发送数据请求,让数据显示在界面上),包括响应View对象触发的各类事件和管理着模型对象和视图层间的数据流动。

的确像那么回事,但是,其实View对应于布局文件来说的话,能做的事情特别少,关于该布局文件中的数据绑定的操作,事件处理的代码全部都在Activity中,造成了Activity既像View又像Controller(当然了Data-Binder的出现,可能会让View更像View吧),这就造成了activity类的代码量巨大,维护起来特别麻烦!

优点:Model层和View层不直接交互,把UI界面和业务逻辑、数据处理分离。
缺点:Activity控制器中有很多显示UI的代码,因此View视图和控制器Controller不是完全分离的,也就是说View视图和控制器Controller绑定在一个类中,当布局复杂的时候,Activity会非常冗余繁杂,解耦不完美。

1.视图与控制器间的过于紧密的连接
视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
2.视图对模型数据的低效率访问
依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

关于MVP

  • MVP 全称是Model - View - Presenter ,是模型(model)-视图(view)-呈现器(presenter)的缩写。
  • Moder:业务逻辑和数据处理(数据库存储操作,网络数据请求,复杂算法,耗时操作)。
  • View : 对应于Activity,负责View的绘制以及与用户交互
  • Presenter:负责完成View于Model间的交互 (有一点还需要注意,presenter是双向绑定的关系,因此,在设计的时候就要注意接口和抽象的使用,尽可能的降低代码的耦合度,这也是mvp的宗旨)

逻辑:将Activity(也就是将View和Controller合并为View)作为View,Model不变,并添加Presenter;View和Model不直接交互,而是使用Presenter作为桥梁。Presenter同时拥有View和Model的Interface引用,而View层有Presenter的Interface引用。当View层需要展示数据的时候,会调用Presenter层的接口,然后Presenter会调用Model请求数据,当Model层数据加载成功后会调用Presenter的回调方法通知Presenter层数据加载完毕,最后Presenter层会调用View层的接口将加载的数据展示给用户。

优点:将View和Moder完全分离,互不依赖。
缺点 : 由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了

三 MVP 与 MVC 区别

  • MVC中的Moder、View和Controller两两之间都有联系。
  • 这里写图片描述
  • MVP中的Moder与View不能直接联系,只能通过Presenter发生关系。
  • 这里写图片描述
    最明显的区别就是,MVC中是允许Model和View进行交互的,MVP中很明显,Model与View之间的交互由Presenter完成。还有一点就是Presenter与View之间的交互是通过接口的。

四 登录经典示例说明MVP架构
这里写图片描述
这里写图片描述
项目结构:
这里写图片描述
(一)Model

创建User模型实体类:
package com.example.mvp_login.Model;

/**
 * create by AND
 * 封装用户信息
 */
public class User {
    private String username;
    private String userpassword;

    public User(String username, String userpassword) {
        this.username = username;
        this.userpassword = userpassword;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", userpassword='" + userpassword + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUserpassword() {
        return userpassword;
    }

    public void setUserpassword(String userpassword) {
        this.userpassword = userpassword;
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
创建Model业务逻辑接口类IUserLogin :
package com.example.mvp_login.Model;

/**
 * 用户登录接口:定义一个业务逻辑的实现方法
 */

public interface IUserLogin {
    /**
     * @param user            登录需要的用户信息
     * @param onLoginListener 登录需要的监听事件
     */
    void login(User user, OnLoginListener onLoginListener);
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
定义Model业务逻辑实现类
package com.example.mvp_login.Model;

/**
 * 用户登录的具体实现类,实现定义的接口,毕竟,接口里方法是没有办法直接使用的
 * 在这里实现具体的登录操作
 */

public class UserLogin implements IUserLogin {
    @Override
    public void login(final User user, final OnLoginListener onLoginListener) {
        //模拟子线程耗时操作
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //模拟登录成功
                if (user.getUsername().equals("devil") && user.getUserpassword().equals("123456")) {
                    //登录成功
                    onLoginListener.loginSuccess();
                } else {
                    //登录失败
                    onLoginListener.loginFailed();
                }
            }
        }.start();
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
定义Model成功失败回调类接口OnLoginListener:
package com.example.mvp_login.Model;

/**
 * 用户登录是否成功的监听
 * 里面的方法不需要参数,也不需要回传数据,只需随时调用
 */

public interface OnLoginListener {
    //登录成功
    void loginSuccess();

    //登录失败
    void loginFailed();
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

实体类,业务类接口,业务类,login()方法一般是连接服务器的,是个耗时操作,所以开辟子线程,Thread.sleep(2000)模拟耗时,由于是耗时操作,所以通过一个回调接口来通知登录的状态。

( 二 ) View
Presenter与View交互是通过接口。所以我们这里需要定义一个ILoginView,难点就在于应该有哪些方法

package com.example.mvp_login.View;

/**
 * view层,顾名思义,视图层,针对的是activity和fragment;
 * 负责从视图中获取相应的数据和更新UI
 */

public interface UserLoginView {
    //显示进度条
    void showLoadding();

    //隐藏进度条
    void hideLoadding();

    //获取用户输入的信息
    String getUserName();

    //获取用户输入的信息
    String getUserPassword();

    //登陆成功
    void toActivity();

    //登陆失败了
    void loadFailed();

    //重置信息
    void clearUserMessage();
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

有了接口,实现就太好写了~~~
观察功能上的操作,然后考虑:

  • 该操作需要什么?(getUserName, getPassword)
  • 该操作的结果,对应的反馈?(toMainActivity, showFailedError)
  • 该操作过程中对应的友好的交互?(showLoading, hideLoading)

创建xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.mvp_login.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="登录"
        android:textSize="25sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户名"
            android:textSize="20sp" />

        <EditText
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:focusable="true"
            android:focusableInTouchMode="true" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="密码"
            android:textSize="20sp" />

        <EditText
            android:id="@+id/userpassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:focusable="true"
            android:focusableInTouchMode="true" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center">

        <Button
            android:id="@+id/login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="登录" />

        <Button
            android:id="@+id/clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="重置" />
    </LinearLayout>

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="invisible" />

</LinearLayout>

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

View的实现类,就是Activity,其实MVP中的View其实就是Activity|Fragment。

package com.example.mvp_login;

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

import com.example.mvp_login.Presenter.UserLoginPresenter;
import com.example.mvp_login.View.UserLoginView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener, UserLoginView {

    private EditText mUsername;
    private EditText mUserpassword;
    private Button mLogin;
    private Button mClear;
    private ProgressBar mProgress;
    private UserLoginPresenter userLoginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//初始化视图

    }

    private void initView() {
        mUsername = (EditText) findViewById(R.id.username);
        mUserpassword = (EditText) findViewById(R.id.userpassword);
        mLogin = (Button) findViewById(R.id.login);
        mLogin.setOnClickListener(this);
        mClear = (Button) findViewById(R.id.clear);
        mClear.setOnClickListener(this);
        mProgress = (ProgressBar) findViewById(R.id.progress);
        //获取P层的实例
        userLoginPresenter = new UserLoginPresenter(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            //登录操作
            case R.id.login:
                // TODO 18/01/06
                //调用P层的登录方法
                userLoginPresenter.login();
                break;
            case R.id.clear:
                // TODO 18/01/06
                //调用P层的重置方法
                userLoginPresenter.clear();
                break;
            default:
                break;
        }
    }

    @Override
    public void showLoadding() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mProgress.setVisibility(View.VISIBLE);
            }
        });
    }

    @Override
    public void hideLoadding() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mProgress.setVisibility(View.INVISIBLE);
            }
        });

    }

    @Override
    public String getUserName() {
        return mUsername.getText().toString();
    }

    @Override
    public String getUserPassword() {
        return mUserpassword.getText().toString();
    }

    @Override
    public void toActivity() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "Login Success", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void loadFailed() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "Login Failed", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void clearUserMessage() {
        mUsername.setText("");
        mUserpassword.setText("");
    }

}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120

Activity中实现我们定义的业务逻辑接口,然后接口引导我们去完成操作。

(三)Presenter
Presenter是用作Model和View之间交互的桥梁
其实也是主要看该功能有什么操作,本例只有两个操作:login()和clear()。

package com.example.mvp_login.Presenter;

import com.example.mvp_login.Model.IUserLogin;
import com.example.mvp_login.Model.OnLoginListener;
import com.example.mvp_login.Model.User;
import com.example.mvp_login.Model.UserLogin;
import com.example.mvp_login.View.UserLoginView;

/**
 * author:Created by AND on 2018/1/7.
 * 连接view层和model的桥梁,--->中介的性质
 * Presenter是用作Model和View之间交互的桥梁,那么应该有什么方法呢?
 * 其实也是主要看该功能有什么操作,比如本例,两个操作:login和clear。
 */
public class UserLoginPresenter {
    private IUserLogin userLogin;//model层
    private UserLoginView userLoginView;//view层

    //v层的有参构造--->因为viewactivity调用时只需要传递上下文
    public UserLoginPresenter(UserLoginView userLoginView) {
        this.userLoginView = userLoginView;
        this.userLogin = new UserLogin();
    }

    //供activity调用的登录方法
    public void login() {
        //登录开始,显示进度条
        userLoginView.showLoadding();
        User user = new User(userLoginView.getUserName(), userLoginView.getUserPassword());
        //m层登录业务操作类
        userLogin.login(user, new OnLoginListener() {
            @Override
            public void loginSuccess() {
                //登录成功
                userLoginView.toActivity();
                //隐藏进度条
                userLoginView.hideLoadding();
            }

            @Override
            public void loginFailed() {
                //登录失败
                userLoginView.loadFailed();
                //隐藏进度条
                userLoginView.hideLoadding();
            }
        });
    }

    //供activity调用的重置方法
    public void clear() {
        userLoginView.clearUserMessage();
    }
}

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

presenter完成二者的交互工作,那么就需要二者的实现类。大致流程就是从View中获取需要的参数,交给Model去执行具体业务方法,执行的过程中需要的反馈,以及结果,再让View进行对应的显示和页面更新。

五 为什么说MVP是优秀的架构模式?
大家对于MVP的普遍的认识其实还是:“代码很清晰,只不过增加了很多类”。但是回过头来,让你自己想个例子写,就头疼的写不出来
这个模式的确让代码的清晰度有了很大的提升。
总结起来,就这么几点
优点

  • 低耦合:MVP拆分了MVC臃肿的Activity,独立了Model、View、Presenter,并通过接口的方式进行连接,板块化实现了低耦合。
  • 高复用:Presenter、Model与Activity(View)的关系实现了一对多。
  • 易测试:独立了Model、View、Presenter以后,更加方便进行单元测试。
  • 好维护:低耦合就成就了好维护。

缺点

     由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了

  
  
  • 1

猜你喜欢

转载自blog.csdn.net/An_nAl/article/details/87931366