转载于: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;}@Overridepublic 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 {@Overridepublic void login(final User user, final OnLoginListener onLoginListener) {//模拟子线程耗时操作new Thread() {@Overridepublic 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;@Overrideprotected 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);}@Overridepublic 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;}}@Overridepublic void showLoadding() {runOnUiThread(new Runnable() {@Overridepublic void run() {mProgress.setVisibility(View.VISIBLE);}});}@Overridepublic void hideLoadding() {runOnUiThread(new Runnable() {@Overridepublic void run() {mProgress.setVisibility(View.INVISIBLE);}});}@Overridepublic String getUserName() {return mUsername.getText().toString();}@Overridepublic String getUserPassword() {return mUserpassword.getText().toString();}@Overridepublic void toActivity() {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "Login Success", Toast.LENGTH_SHORT).show();}});}@Overridepublic void loadFailed() {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "Login Failed", Toast.LENGTH_SHORT).show();}});}@Overridepublic 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() {@Overridepublic void loginSuccess() {//登录成功userLoginView.toActivity();//隐藏进度条userLoginView.hideLoadding();}@Overridepublic 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

Android中MVP框架理解相关推荐

  1. android项目集成okgo,Android中MVP+RXJAVA+OKGO框架

    [实例简介] Android中MVP+RXJAVA+OKGO框架 Glide的封装 沉浸式状态栏 butterknife 和recyclerview的使用 [实例截图] [核心代码] 882096ee ...

  2. java mvp模式_什么是mvp开发模式?(下面就对Android中MVP做一些阐述)

    MVP作为一种MVC的演化版本在Android开发中受到了越来越多的关注.值得注意的是,MVP不像JavaEE有着SSH这三个成熟框架支持推动着,所以在运用MVP时一定要做好自己的理解,并且尽量预知自 ...

  3. android中mvp封装,android-简单快速封装MVP+Retrofit2.0+Rxjava框架

    1.概述 对于MVP(Model View Presenter)大多数做开发的人都能说出一二,或者看到网上的对mvp的解释,对其意都有大概的了解,但让你真正写一套mvp框架,是不是发现无从下手? 曾几 ...

  4. android 最新框架组合,android 官方mvp框架优化:lifecycle-mvp,像前端那样组合式写页面...

    目录 1 前言 虽然在标题上,自己很随意的起了这么一个名字.其实并不是说它起个英文名就牛逼了.说白了,它其实就是mvp的思想加了lifecycle-component,然后加入了分层的思想,最后用Ty ...

  5. 如何一步一步实现Android的MVP框架

    内容大纲: Android 开发框架的选择 如何一步步搭建分层框架 使用 RxJava 来解决主线程发出网络请求的问题 结语 一.Android开发框架的选择 由于原生 Android 开发应该已经是 ...

  6. Android中的Context理解

    1.sdk当中关于Context的介绍 Interface to global information about an application environment. This is an abs ...

  7. Android中Audio框架

    原址 原文链接 Android中的音频硬件抽象层(HAL)连接android.media中高层的,特定音频框架API到底层的音频驱动和硬件 下列图表描述了音频功能是如何实现的,以及相关实现的相关源代码 ...

  8. Android中Context 的理解

    这里记录Context的原因是新来的同事问我Android Context 怎样理解,我是这样说的,Context 英文是上下文,它是一个抽象的类,加入在MainActivity 中,Context ...

  9. 对android中Zygote的理解

    谈谈对Zygote的简单理解 1. Zygote的作用 启动SystemServer 孵化应用进程 SystemServer也是通过Zygote启动的,因为它也需要Zygote的资源:常用类,JNI函 ...

  10. android surfaceview 技术,Android中SurfaceView的理解和使用

    关于SurfaceView的官方文档地址. 理论概述 SurfaceView是View的子类,它已知的直接子类有GLSurfaceView和VideoView.我们知道每个View都有一个用于绘画的画 ...

最新文章

  1. priority_queue 结构体的优先级设置
  2. CocoStudio资源区导入Plist/PSD文件
  3. response.addCookie(cookie)添加cookie失败.
  4. 图像压缩哪家强?请看这份超详细对比
  5. Struts2(一)— 入门
  6. C++ | 内联函数 inline
  7. Linux最终将会领先于Windows、Mac OS!
  8. [工具]微软的学习平台Microsoft Learn很好用,推荐一下
  9. MySQL函数/数据库函数
  10. 天玑800处理器支持鸿蒙系统吗,为何Redmi Note 9选择天玑800U处理器?和骁龙750G差距多大...
  11. mysql 大小限制_MYSQL 表大小限制
  12. 百度手机输入法,如何使用五笔98版?
  13. 【转】C#字符串转换为日期
  14. 调用百度识图api实现识图vue+springboot
  15. Polygon 上 3 款最受欢迎的 GameFi 游戏
  16. 【渝粤教育】广东开放大学 跨文化商务沟通 形成性考核 (42)
  17. 闲鱼直播flutter化实践
  18. uniapp editor编辑器
  19. Lucene学习——IKAnalyzer中文分词(一)
  20. Evaluations

热门文章

  1. hadoopsnappy解压_Hadoop的Snappy安装配置
  2. 【数字IC/FPFA】时序约束--时钟约束
  3. PS给图片四周加上圆角
  4. HTML朗读可以用英文吗,关于英语朗读的方法技巧
  5. 经纬度计算两地之间的距离(原理与方法)
  6. jQuery.siblings() 函数详解
  7. 计算机画图照片大小,电脑自带的画图工具怎么调整图片的大小?
  8. 深入理解DirectX D3D9
  9. python软著申请_软著申请流程时间
  10. 26367411153598389kygoq