今天来看看Android的MVP模式,使用框架开发,开发速度以及代码的目录结构会别有一番风格。
Google的demo:https://github.com/googlesamples/android-architecture
目前已经完成的示例有
todo-mvp(mvp基础架构示例)
todo-mvp-loaders(基于mvp基础架构项目,获取数据部分使用了Loaders架构)
todo-mvp-databinding(基于mvp基础架构项目,使用了数据绑定组件)
todo-mvp-clean(基于mvp基础架构项目,使用了clean架构的概念)
todo-mvp-dagger(基于mvp基础架构项目,使用了dagger2进行依赖注入)
一张网上的图了解MVP:
这种分层的好处:层与层之间的耦合性低,模块的复用性高,可维护性更好,每层可以单独存在,这样可测性更好
model为上层提供数据,model处理上层传递的数据
presenter是处于mvp的中间层,在view和model中起一个承上启下的作用。presenter会把view交给自己的命令进行一定的校验等操作交给model处理,会把model处理的结果交给view,presenter会根据获取的数据成功与否来通知view应该是显示成功界面还是失败界面。
view就是用户直接看到的界面,一个view可以同时拥有多个presenter,也可以只有一个presenter。
Android中的Activity,Fragment在mvp中是作为view来使用的,各种Adapter是放在view层的。
以下就以登陆界面看看怎么使用MVP结构,先看看一般的(新手)登陆写法
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class LoginActivity extends Activity {
private EditText mUserName, mPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
}
private void initView() {
// findViewById.....
}
/**
* 登陆按钮,需要验证输入的数据合法性
* @param view
*/
public void loginCheck(View view){
String userName = mUserName.getText().toString();
String password = mPassword.getText().toString();
//验证用户输入的密码是否合法
if(!validate(userName) || !validate(password)){
//告诉用户输入的用户名或密码不合法
} else{
//开始登陆
login(userName,password);
}
}
/**
* 访问网络进行登陆,分登陆成功和失败,显示不同的界面
* @param userName
* @param password
*/
private void login(String userName, String password) {
}
/**
* 验证数据的合法性 非空等等
* @param userName
* @return
*/
private boolean validate(String userName) {
// TODO Auto-generated method stub
return false;
}
}
这种写法不是不可以,而是所有的功能都写在了一起,耦合性太高,然后可能还会狡辩,你看我所有功能都分方法了,方法分的多清楚...不说了,看看使用MVP来代码重构
从model层开始 ,LoginModel 使用 enum 做一个单例
import java.io.UnsupportedEncodingException;
import org.apache.http.entity.StringEntity;
import android.util.Log;
import com.example.login_mvp.model.interfaces.HttpListener;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.RequestParams;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest.HttpMethod;
/**
* 联网获取数据,使用xUtil工具
* enum 实现单例
* @author chenling0418
*
*/
public enum LoginModel {
/** enum 实现单例
* 定义一个枚举的元素,它就代表了LoginModel的一个实例。
*/
INSTANCE;
/** 登陆
* @param name 姓名
* @param password 密码
* @param loginListener 登陆结果的回调
*/
public void login(String name,String password,final HttpListener loginListener){
// 使用xUtil ,HttpUtils也应该只有一个实例,这里是模拟
HttpUtils httpUtils = new HttpUtils();
RequestParams requestParams = new RequestParams();
// 建议使用 JSON交互 JsonObject
try {
requestParams.setBodyEntity(new StringEntity("your params"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.i("slack", "error:"+e.toString());
}
// send(HttpMethod method, String url, RequestParams params, RequestCallBack<Object> callBack)
httpUtils.send(HttpMethod.POST, "your webservice host", requestParams, new RequestCallBack<String>() {
@Override
public void onFailure(HttpException arg0, String error) {
// TODO Auto-generated method stub
if(loginListener != null){
loginListener.onFailure(error);
}
}
@Override
public void onSuccess(ResponseInfo<String> result) {
// TODO Auto-generated method stub
if(loginListener != null){
loginListener.onSuccess(result.result);
}
}
});
}
}
接着是
Presenter ,LoginPresenter
import com.example.login_mvp.model.LoginModel;
import com.example.login_mvp.model.bean.UserInfo;
import com.example.login_mvp.model.interfaces.HttpListener;
import com.example.login_mvp.presenter.interfaces.ILoginPresenter;
import com.example.login_mvp.view.interfaces.ILoginView;
/**
* 登录的 具体的presenter,负责协调 view 和 model
* presenter 会有多个,负责不同功能
* @author chenling0418
*
*/
public class LoginPresenter implements ILoginPresenter{
private ILoginView mLoginView;
@Override
public void init(ILoginView view) {
mLoginView = view;
mLoginView.initView();//初始化view(findById...),此处其实是回调
}
@Override
public void login(String name, String password) {
//验证name,password的合法性,
if(validate(name) && validate(password)){
// 获取单例
LoginModel.INSTANCE.login(name, password, new HttpListener() {
@Override
public void onSuccess(String result) {
// 这里假设成功后返回的是用户的信息(json) 需要对result处理然后传入 onShowSuccessLoginView
UserInfo userInfo = new UserInfo();
//下面的代码在ui线程中执行,这就不写具体的实现了
mLoginView.onShowSuccessLoginView(userInfo);
mLoginView.dissLoginingView();
}
@Override
public void onFailure(String error) {
//下面的代码在ui线程中执行,这就不写具体的实现了
mLoginView.onShowFailedLoginView();
mLoginView.dissLoginingView();
}
});
}else{
//假设1代表账号,密码不合法
mLoginView.onShowLoginCheckErrorView();
}
}
/**
* 验证数据的合法性 非空等等
* @param userName
* @return
*/
private boolean validate(String userName) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onStop() {
// TODO Auto-generated method stub
}
@Override
public void onResume() {
// TODO Auto-generated method stub
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
}
@Override
public void onPause() {
// TODO Auto-generated method stub
}
@Override
public void onStart() {
// TODO Auto-generated method stub
}
}
View 层,这里做一个基类BaseActivity
import java.util.HashSet;
import java.util.Set;
import com.example.login_mvp.presenter.interfaces.IPresenter;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
/**
* BaseActivity 所有Activity的基类
*
* @author chenling0418
*
*/
public abstract class BaseActivity extends FragmentActivity {
private Set<IPresenter> mAllPresenters = new HashSet<IPresenter>();
/**
* * 获取layout的id,具体由子类实现
*
* @return
*/
protected abstract int getLayoutResId();
/**
* 需要子类来实现,获取子类的IPresenter,一个activity有可能有多个IPresenter
*/
protected abstract IPresenter[] getPresenters();
// 初始化presenters,
protected abstract void onInitPresenters();
/**
* * 从intent中解析数据,具体子类来实现
*
* @param argIntent
*/
protected void parseArgumentsFromIntent(Intent argIntent) {
}
/**
* 此处是把所有的presenter 添加到 mAllPresenters 集合
*
* 这里用到了设计模式里的 观察者模式,对象之间的一对多的依赖,这样一来,当一个对象改变时,
* 它的所有的依赖者都会收到通知并自动更新
*
*/
private void addPresenters() {
IPresenter[] presenters = getPresenters();
if (presenters != null) {
for (int i = 0; i < presenters.length; i++) {
mAllPresenters.add(presenters[i]);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
if (getIntent() != null) {
parseArgumentsFromIntent(getIntent());
}
addPresenters();
onInitPresenters();
}
@Override
protected void onResume() {
super.onResume();
// 依次调用IPresenter的onResume方法
for (IPresenter presenter : mAllPresenters) {
if (presenter != null) {
presenter.onResume();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 依次调用IPresenter的onResume方法
for (IPresenter presenter : mAllPresenters) {
if (presenter != null) {
presenter.onDestroy();
}
}
}
// ...其他生命周期方法也是类似,调用IPresenter中相应的生命周期方法...
}
具体的实现的类
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import com.example.login_mvp.R;
import com.example.login_mvp.model.bean.UserInfo;
import com.example.login_mvp.presenter.LoginPresenter;
import com.example.login_mvp.presenter.interfaces.IPresenter;
import com.example.login_mvp.view.interfaces.ILoginView;
/**
* view 只专心负责各种 显示
*
* @author chenling0418
*
*/
public class LoginActivity extends BaseActivity implements ILoginView{
private LoginPresenter mLoginPresenter = new LoginPresenter();
private EditText mUserName, mPassword;
private Button mLogin;
@Override
public void initView() {
// ...初始化view的代码... findViewById...
mLogin = (Button)findViewById(R.id.login);
mLogin.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
mLoginPresenter.login(mUserName.getText().toString(), mPassword.getText().toString());
}
});
}
@Override
public void onShowSuccessLoginView(UserInfo userInfo) {
// ....显示登录成功界面....
}
@Override
public void onShowFailedLoginView() {
// ...显示登录失败界面...
}
@Override
public void showLoginingView() {
// ...显示登录进度条对话框...
}
@Override
public void dissLoginingView() {
// ...消失登录进度条对话框...
}
@Override
protected int getLayoutResId() {
return R.layout.activity_login;
}
@Override
protected IPresenter[] getPresenters() {
return new IPresenter[]{ mLoginPresenter};
}
@Override
protected void onInitPresenters() {
mLoginPresenter.init(this);
}
@Override
public void onShowLoginCheckErrorView() {
// ...显示填入信息错误界面...
}
}
我只能表示看懂了,代码里有注释
参考:http://android.jobbole.com/83294/#rd(特别感谢作者的无私奉献)
GitHub: https://github.com/CL-window/mvpTest/