MVP介绍:
View 对应于Activity,负责View的绘制以及与用户交互
Model 依然是业务逻辑和实体模型
Presenter 负责完成View于Model间的交互
(view不可以直接和model交互,而是通过presenter作为中介进行交互的)
概述:
View :是指显示数据并且和用户交互的层。在安卓中,它们可以是一个Activity,一个Fragment,一个android.view.View或者是一个Dialog。
Model :是数据源层。比如数据库接口或者远程服务器的api。
Presenter:是从Model中获取数据并提供给View的层,Presenter还负责处理后台任务。
MVP是一个将后台任务和activities/views/fragment分离的方法,让它们独立于绝大多数跟生命周期相关的事件。这样应用就会变得更简单,整个应用的稳定性提高10倍以上,代码也变得更短,可维护性增强
在MVP模式里通常包含4个要素:
View :负责绘制UI元素、与用户进行交互(在Android中体现为Activity);
View interface :需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试;
Model :负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);
Presenter :作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
MVP优缺点
优点:
1-降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Modle
2-模块职责划分明显,层次清晰
3-隐藏数据
4-Presenter可以复用,一个Presenter可以用于多个View,而不需要更改Presenter的逻辑(当然是在View的改动不影响业务逻辑的前提下)
5-利于测试驱动开发。以前的Android开发是难以进行单元测试的(虽然很多Android开发者都没有写过测试用例,但是随着项目变得越来越复杂,没有测试是很难保证软件质量的;而且近几年来Android上的测试框架已经有了长足的发展——开始写测试用例吧),在使用MVP的项目中Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用逻辑的正确性。
6-View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的View组件。
7-代码灵活性
缺点:
1-Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。
2-由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。
3-如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。
4-额外的代码复杂度及学习成本。
MVP实现登录界面
一,定义一个Servive接口用来请求数据
设置Bean类
public interface ApiService {
StringbaseUrl ="https://www.wanandroid.com/user/";
@POST("login")
@FormUrlEncoded
Observable login(@Field("username") String username,@Field("password") String password);
}
二,建包(callback)定义LoginCallback接口类
public interface LoginCallback {
void onSucces(LoginBean loginBean);
void onFail(String error);
}
三,建包(model)
①定义LoginModel接口类
public interface LoginModel {
void login(LoginCallback loginCallback,String username,String password);
}
②定义ImpLoginModel类实现LoginModel
public class ImpLoginModelimplements LoginModel {
@Override
public void login(final LoginCallback loginCallback, String username, String password) {
Retrofit retrofit =new Retrofit.Builder()
.baseUrl(ApiService.baseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService apiService = retrofit.create(ApiService.class);
Observable observable = apiService.login(username, password);
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(LoginBean loginBean) {
int code = loginBean.getErrorCode();
if (code ==0) {
loginCallback.onSucces(loginBean);
}else {
loginCallback.onFail(loginBean.getErrorMsg());
}
}
@Override
public void onError(Throwable e) {
loginCallback.onFail("登陆失败" + e.getMessage());
}
@Override
public void onComplete() {
}
});
}
}
四,建包(presenter)
①定义LoginPresenter接口类
public interface LoginPresenter {
void login(String username, String password);
}
②定义ImpLoginPresenter实现LoginPresenter
public class ImpLoginPresenterimplements LoginPresenter, LoginCallback {
private LoginViewloginView;
private final ImpLoginModelimpLoginModel;
public ImpLoginPresenter(LoginView loginView) {
this.loginView = loginView;
impLoginModel =new ImpLoginModel();
}
@Override
public void login(String username, String password) {
if (TextUtils.isEmpty(username)) {
loginView.onFail("请输入账号");
return;
}
if (TextUtils.isEmpty(password)) {
loginView.onFail("请输入密码");
return;
}
impLoginModel.login(this, username, password);
}
@Override
public void onSucces(LoginBean loginBean) {
int code = loginBean.getErrorCode();
if (code ==0) {
loginView.onSucces(loginBean);
}else {
loginView.onFail(loginBean.getErrorMsg());
}
}
@Override
public void onFail(String error) {
loginView.onFail(error);
}
}
五,建包(view)创建接口服务类LoginView
public interface LoginView {
void onSucces(LoginBean loginBean);
void onFail(String error);
}
六,Activity类中
首先创建ImpLoginPresenter对象
实现里面的两个方法
@Override
public void onSucces(LoginBean loginBean) {
int code = loginBean.getErrorCode();
if(code==0){
Toast.makeText(this,"登录成功", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFail(String error) {
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
}
在按钮监听中调运login方法
private void login() {
// validate
String name =et_name.getText().toString().trim();
String pwd =et_pwd.getText().toString().trim();
presenter.login(name, pwd);
// TODO validate success, do something
}