什么是MVP
mvc
模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等
视图(View):负责界面数据的展示,与用户进行交互,主要是xml文件;
控制器(Controller):控制(Controller)可以理解为从用户接收请求, 将模型与视图匹配在一起,共同完成用户的请求。划分控制层的作用也很明显,它清楚地告诉你,它就是一个分发器,选择什么样的模型,选择什么样的视图,可以完成什么样的用户请求。控制层并不做任何的数据处理。例如,用户点击一个连接,控制层接受请求后, 并不处理业务信息,它只把用户的信息传递给模型,告诉模型做什么,选择符合要求的视图返回给用户。因此,一个模型可能对应多个视图,一个视图可能对应多个模型。
mvp
MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。
模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;
视图(View):负责界面数据的展示,与用户进行交互,主要是指Fragment ,Activity;
主持人(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来。
如下图所示,View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这就是MVP模式的整个核心过程。
这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。
- View发生改变,不影响model
- model发生改变,不影响View
- presenter发生改变,只需要改persenter
下面看下MVP模式在具体项目(登录)中的使用。
1.model层描述和具体代码
- 提供我们想要展示在view层的数据和具体登陆业务逻辑处理的实现
package com.delta.ams.mvpdemo.model.login;
import android.content.Context;
public interface LoginModel {
public void login(String username, String password, Context context);
public void logout();
}
LoginModelImp
package com.delta.ams.mvpdemo.model.login;
import android.content.Context;
import android.util.Log;
import com.android.volley.AuthFailureError;
import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.delta.ams.mvpdemo.MyApplication;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class LoginModelImpl implements LoginModel {
private OnLoginListener mOnLoginListener;
private final static String LOG_TAG = LoginDaoImpl.class.getSimpleName();
public void setOnLoginListener(OnLoginListener mOnLoginListener) {
this.mOnLoginListener = mOnLoginListener;
}
public void login(String username, String password, final Context context) {
Map<String, String> param = new HashMap<>();
param.put("name", username);
param.put("pwd", password);
JSONObject object = new JSONObject(param);
JsonObjectRequest request = new JsonObjectRequest(Method.POST,
com.delta.ams.mvpdemo.Common.URL.LOGIN_URL, object, new Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
if (mOnLoginListener != null) {
if (response != null) {
mOnLoginListener.onSuccess(response
.toString());
}
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error != null) {
mOnLoginListener.onFailed(error.getMessage());
}
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return super.getHeaders();
}
};
Log.i(LOG_TAG, "Body" + new String(request.getBody()));
try {
Log.i(LOG_TAG, request.getHeaders().toString());
} catch (AuthFailureError e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MyApplication.getQueue().add(request);
}
@Override
public void logout() {
}
public interface OnLoginListener {
void onSuccess(String cookies);
void onFailed(String errorMsg);
}
}
2.view层描述和具体代码
- 负责显示数据、提供友好界面跟用户交互就行。MVP下Activity和Fragment以及View的子类体 现在了这一 层,Activity一般也就做加载UI视图、设置监听再交由Presenter处理的一些工作,所以也就需要持有相应Presenter的引用。本层所需要做的操作就是在每一次有相应交互的时候,调用presenter的相关方法就行。(比如说,button点击)
package com.delta.ams.mvpdemo.Base;
/**
* Created by V.wenju.tian on 2016/8/18.
*/
public interface BaseView {
void close();
void showProgress(String msg);
void showProgress(String msg, int progress);
void hideProgress();
void showErrorMessage(String msg, String content);
}
interface LoginView extends BaseView {
void LoginSucess(String response);
void LoginError(String errorMessage);
}
package com.delta.ams.mvpdemo;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.delta.ams.mvpdemo.Base.AbsBaseActivity;
import com.delta.ams.mvpdemo.ui.login.login_contact.LoginContract;
import com.delta.ams.mvpdemo.ui.login.login_contact.LoginPersenter;
public class MainActivity extends implements LoginView, View.OnClickListener {
private EditText et_name;
private EditText et_password;
private Button bt_login;
private LoginPersenter mLoginPersenter;
private String mUserName;
private String mUserPassword;
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
mLoginPersenter
= new LoginPersenter();
mLoginPersenter.attachView(this);
et_name = (EditText) findViewById(R.id.et_name);
et_password = (EditText) findViewById(R.id.et_password);
bt_login = (Button) findViewById(R.id.bt_login);
bt_login.setOnClickListener(this);
}
@Override
public void LoginSucess(String response) {
Log.d("dsd", "LoginSucess() called with: " + "response = [" + response + "]");
Toast.makeText(this, "正常", Toast.LENGTH_SHORT).show();
}
@Override
public void LoginError(String errorMessage) {
Log.d("dsd", "LoginError: ");
Toast.makeText(this, "失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View v) {
mUserName = et_name.getText().toString().trim();
mUserPassword = et_password.getText().toString().trim();
mLoginPersenter.login(mUserName, mUserPassword,this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mLoginPersenter.detachView();
}
}
3.Presenter
- Presenter扮演着view和model的中间层的角色。获取model层的数据之后构建view层;也可以收到view层UI上的反馈命令后分发处理逻辑,交给model层做业务操作。它也可以决定View层的各种操作。
package com.delta.ams.mvpdemo.Base;
/**
*
* Class Note:
* MVP中所有Presenter的接口,完成view的绑定和解除
*
*/
public interface BasePresenter<T extends BaseView> {
/**
* 注入View,使之能够与View相互响应
*
* @param view
*/
void attachView(T view);
/**
* 释放资源
*/
void detachView();
}
package com.delta.ams.mvpdemo.ui.login.login_contact;
import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;
import com.delta.ams.mvpdemo.model.login.LoginDaoImpl;
/**
* Created by V.wenju.tian on 2016/8/18.
*/
public class LoginPersenter implements LoginPersenter {
private LoginView mView;
private Context mContext;
private LoginModelImpl mLoginModel;
@Override
public void attachView(LoginView view) {
this.mView = view;
mLoginModel = new LoginModelImpl();
}
@Override
public void detachView() {
mView = null;
}
@Override
public void login(String name, String passWord, Context mcontext) {
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(passWord)) {
Toast.makeText(mContext, "sdsdd", Toast.LENGTH_SHORT).show();
} else {
mLoginModel.login(name, passWord, mcontext);
mView.showProgress("登陆中。。");
mLoginModel.setOnLoginListener(new LoginModelImpl.OnLoginListener() {
@Override
public void onSuccess(String cookies) {
mView.LoginSucess(cookies);
mView.hideProgress();
}
@Override
public void onFailed(String errorMsg) {
mView.LoginError(errorMsg);
mView.hideProgress();
}
});
}
}
}
4.类图
5.流程
业务流程
1.Activity做了一些UI初始化的东西并需要实例化对应LoginPresenter的引用和实现 LoginView的接口,登陆按钮按下后即接收到登陆的事件,在onClick里接收到即通过LoginPresenter的引用把它交给LoginPresenter处理。
2. 然后LoginPresenter显示进度条并且把逻辑交给我们的Model去处理,也就是这里面的LoginModel,(LoginModel现类LoginModelImpl)。
3.这时候通过onLoginListener回调传回所要的结果给Pesenter,然后在做相应的处理。
4. presenter把回调结果通知给mView,从而实现视图的更新。