今天来看看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:

MVP的架构思路 mvp架构模式_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来代码重构

MVP的架构思路 mvp架构模式_基础架构_02

从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/