几天给大家简单的介绍一下MVP,并且简单的使用和了解MVP的框架和用法。一下是对MVP的简单的概括。
一、MVP介绍
随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MVP(Model-View-Presenter)模式应运而生。
在MVP模式里通常包含4个要素:
(1)
View
:负责绘制UI元素、与用户进行交互(在Android中体现为Activity);
(2
)
View interface
:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试;
(3)
Model
:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);
(4)
Presenter
:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
二、为什么使用MVP模式
在Android开发中,Activity并不是一个标准的MVC模式中的Controller,
它的首要职责是加载应用的布局和初始化用户界面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。当我们将其中复杂的逻辑处理移至另外的一个类(Presneter)中时,Activity其实就是MVP模式中View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由Presenter处理).
另外,回想一下你在开发Android应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到Android模拟器或真机上,然后通过模拟用户操作进行测试?然而由于Android平台的特性,每次部署都耗费了大量的时间,这直接导致开发效率的降低。而在MVP模式中,处理复杂逻辑的Presenter是通过interface与View(Activity)进行交互的,这说明了什么?说明我们可以通过自定义类实现这个interface来模拟Activity的行为对Presenter进行单元测试,省去了大量的部署及测试的时间。
三、MVP与MVC的异同
MVC模式与MVP模式都作为用来分离UI层与业务层的一种开发模式被应用了很多年。在我们选择一种开发模式时,首先需要了解一下这种模式的利弊:
无论MVC或是MVP模式都不可避免地存在一个弊端:
额外的代码复杂度及学习成本。
这就导致了这两种开发模式也许并不是很小型应用。
但比起他们的优点,这点弊端基本可以忽略了:
(1)降低耦合度
(2)模块职责划分明显
(3)利于测试驱动开发
(4)代码复用
(5)隐藏数据
(6)代码灵活性
对于MVP与MVC这两种模式,它们之间也有很大的差异。
有一些程序员选择不使用任何一种模式,有一部分原因也许就是不能区分这两种模式差异
。以下是这两种模式之间最关键的差异:
MVP模式:
- View不直接与Model交互,而是通过与Presenter交互来与Model间接交互
- Presenter与View的交互是通过接口来进行的,更有利于添加单元测试
- 通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑
MVC模式:
- View可以与Model直接交互
- Controller是基于行为的,并且可以被多个View共享
- 可以负责决定显示哪个View
接下来就简单的实现一个登录注册的功能,:
先看一下建包:model,presenter,view
在model和view中,分别建两个接口,Imodel.class,Iview.class
package model;
import bean.User;
/**
* Created by 笔片 on 2017/11/1.
*/
public interface Imodel {
/**
* 登录
*
* @param user
* @return 约定返回"true"为登录成功,其他为登录失败的错误信息
*/
String login(User user);
}
package view;
/**
* Created by 笔片 on 2017/11/1.
*/
public interface Iview {
/**
* 登录成功
*/
void onLoginSuccess();
/**
* 登录失败
*
* @param error
*/
void onLoginFailed(String error);
}
然后分别建两个类,usermodel,userview,来分别实现这两个接口
package view;
/**
* Created by 笔片 on 2017/11/1.
*/
public class userview implements Iview{
@Override
public void onLoginSuccess() {
}
@Override
public void onLoginFailed(String error) {
}
}
usermodel:
package model;
import com.bwie.test.mymvptext.API;
import java.io.IOException;
import java.util.HashMap;
import bean.User;
import bean.UserBean;
import okhttp3.Call;
import utils.GsonObjectCallback;
import utils.OkHttp3Utils;
/**
* Created by 笔片 on 2017/11/1.
*/
public class usermodel implements Imodel{
String result;
@Override
public String login(User user) {
HashMap<String,String> map = new HashMap<>();
map.put("mobile",user.getUsername());
map.put("password",user.getPassword());
OkHttp3Utils.getInstance().doPost(API.LOGIN, map, new GsonObjectCallback<UserBean>() {
@Override
public String onUi(UserBean userBean) {
String code = userBean.getCode();
if (code.equals("0")){
result = "true";
}else {
result = "fales";
}
return code;
}
@Override
public void onFailed(Call call, IOException e) {
}
});
return result;
}
}
这其中,用到了Okhttp来请求网络数据,所以导入一些OkHttp的相关内容,我这里边是导入了一个依赖,各种办法都可以,不知道的人可以去
http://blog.csdn.net/pentablet/article/details/78273444看看,这里边有队OkHttp的详细介绍和代码,咋们继续,这里的User类,是网络接口实体类,自己找一个借口,自己封装就可以,就不粘贴了,然后自己再建一个Api类,里边用来存放网络接口。
Api.class:"LOGIN"是我的接口,
package com.bwie.test.mymvptext;
/**
* Created by 笔片 on 2017/11/1.
*/
public class API {
public static final String LOGIN = "http://120.27.23.105/user/login";
}
由于是要实现登录功能,所以用户要输入用户和密码,同时,我们也要获取输入的内容,与网络接口进行比较,所以,要建一个UserBean的实体类,来存放用户名和密码:
UserBean.class:
package bean;
/**
* Created by 笔片 on 2017/11/1.
*/
public class User {
private String username;
private String password;
@Override
public String toString() {
return "User{" +
"password='" + password + '\'' +
", username='" + username + '\'' +
'}';
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public User(String password, String username) {
this.password = password;
this.username = username;
}
}
在presenter包中建 UserPresenter类:
UserPresenter.class:
package presenter; import android.os.Handler; import android.os.Looper; import bean.User; import model.Imodel; import model.usermodel; import view.Iview; /** * Created by 笔片 on 2017/11/1. */ public class UserPresenter { private final Iview userView; private final Imodel userModel; public UserPresenter(Iview userView) { this.userView = userView; this.userModel = new usermodel(); } /** * 登录 */ public void Login(final User user) { final String res = userModel.login(user); if ("true".equals(res)) { userView.onLoginSuccess(); } else { userView.onLoginFailed(res); } } }
最后就剩下主函数了:
MainActivity.class:
package com.bwie.test.mymvptext; import android.app.ProgressDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.EditText; import android.widget.Toast; import bean.User; import presenter.UserPresenter; import view.Iview; public class MainActivity extends AppCompatActivity implements Iview , View.OnClickListener{ private UserPresenter userPresenter; private EditText etUserName; private EditText etPassword; private ProgressDialog loginProgreess; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etUserName = (EditText) findViewById(R.id.username); etPassword = (EditText) findViewById(R.id.password); findViewById(R.id.button).setOnClickListener(this); userPresenter = new UserPresenter(this); } @Override public void onLoginSuccess() { loginProgreess.dismiss(); Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show(); } @Override public void onLoginFailed(String error) { loginProgreess.dismiss(); Toast.makeText(MainActivity.this, "====", Toast.LENGTH_SHORT).show(); } @Override public void onClick(View view) { String username = etUserName.getText().toString().trim(); String password = etPassword.getText().toString().trim(); loginProgreess = ProgressDialog.show(this, "登录", "正在登录..."); userPresenter.Login(new User(password,username)); } }
最后布局文件也很简单:
activity_main.xml:
好了,这就是简单的MVP的介绍与简单的登录功能。<?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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.bwie.test.mymvptext.MainActivity"> <EditText android:id="@+id/username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="63dp" android:ems="10" android:hint="请输入用户名" android:inputType="textPersonName" /> <EditText android:id="@+id/password" android:hint="请输入密码" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignEnd="@+id/username" android:layout_alignRight="@+id/username" android:layout_below="@+id/username" android:layout_marginTop="62dp" android:ems="10" android:inputType="textPassword" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/password" android:layout_centerHorizontal="true" android:layout_marginTop="85dp" android:text="Button" /> </RelativeLayout>