Android架构:MVP模式实例

现在的安卓开发已经很成熟,不像几年前大部分人还处于技术摸索阶段,当技术问题不再是安卓开发的难题时,更多人开始关注架构设计,代码质量,更想易于测试,维护方便,逻辑清晰。大家试图让所有的代码都高度解耦,各层分离,从而达到目的。MVP的架构模式,就这样应运而生。这里我希望以最简单的例子,让大家快速入门MVP模式。

认识MVP

MVP就是Model,View,Presenter的缩写。
对比MVC,MVP模式的特点是V层和M层不可互相接触,需要通过主导器Presenter进行关联,P层进行所有的逻辑处理,V层仅仅负责视图展示,M层负责获取数据。
下面是MVC和MVP对比图:
Android架构:MVP模式实例_第1张图片
图片出处

实例

依然是最简单的登录页面,我通过一个登录业务,来详述MVP在项目中的使用,文后会附上代码地址。
Android架构:MVP模式实例_第2张图片

既然要解耦,必然是面向接口的编程,就按照写代码的顺序从Model开始看:
LoginModel.java


/** * Created by yuankundong on 2016/03/03. */
public interface LoginModel {

    void toLogin(String username, String password, OnLoginListener onLoginListener);
}

对于Model层,没有什么多余的方法,就是完成登录,把结果返回给Presenter。这里我又写了一个接口OnLoginListener,为了给Presenter回调,让View和Model分离,当然,如果我们的框架中使用了otto之类的东东,那就不需要这个接口。接着看上面接口的实现:
LoginModelImpl.java

/** * Created by yuankundong on 2016/03/03. */
public class LoginModelImpl implements LoginModel{

    String url = "http://xxxxx";
    public final static int TIME_OUT_CODE = 99;
    public final static String TIME_OUT_MSG = "网络连接不可用,请稍后重试";
    @Override
    public void toLogin(final String username, final String password, final OnLoginListener onLoginListener) {

        StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {

            @Override
            public void onResponse(String response) {

                Type type = new TypeToken<UserBean>() {
                }.getType();
                Gson gson = new Gson();
                UserBean userBean = gson.fromJson(response, type);
                if (userBean.isSuccess()) {
                    onLoginListener.OnSuccess(userBean);
                } else {
                    onLoginListener.OnError(userBean.getCode(), userBean.getMsg());
                }

            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                onLoginListener.OnError(TIME_OUT_CODE, TIME_OUT_MSG);
            }
        }) {
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> map = new HashMap<>();
                map.put("phone", username);
                map.put("password", MD5Utils.stringToMD5(password));
                return map;
            }
        };
        BaseApplication.requestQueue.add(stringRequest);
    }
}

这里使用Volley网络框架,假设登录成功后,返回的json数据是这个类型:

{
    "code": 0,
    "msg": "登录成功" }

那么我的实体Bean可以这么设计:

/** * Created by yuankundong on 2016/03/03. */
public class UserBean {
    private int code;    // 返回码,0为成功
    private String msg;      // 返回信息


    public UserBean(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return code == 0;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

上述代码就完成了Model层,因为Presenter是M和V的联络人,所以,我们再把View层写了,同样,需要先写接口,这个接口里面我们写什么呢,就是在页面上,会发生的所有事,比如,获取用户名密码,点击登录后有进度条提示,登录反馈后取消提示等。

/** * Created by yuankundong on 2016/03/03. */
public interface LoginView {

    //取得用户名
    String getUsername();

    //取得密码
    String getPassword();

    //登录成功后
    void loginSuccess();

    //登录失败后
    void loginError(String msg);

    //展示进度条
    void showProgress();

    //隐藏进度条
    void hideProgress();

    //当需要弹出提示信息时
    void showMessage(String msg);
}

我们能想到的都写进来了,接着我们写这个接口的实现类,就是个Activity。

/** * Created by yuankundong on 2016/03/03. */
public class LoginActivity extends AppCompatActivity implements LoginView{
    TextView username;
    TextView password;
    Button login;
    ProgressDialog dialog;
    LoginPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        presenter = new LoginPresenterImpl(this);
        username = (TextView) findViewById(R.id.tv_username);
        password = (TextView) findViewById(R.id.tv_password);
        login = (Button) findViewById(R.id.btn_login);
        login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.actionLogin();
            }
        });

    }

    @Override
    public String getUsername() {
        return username.getText().toString();
    }

    @Override
    public String getPassword() {
        return password.getText().toString();
    }

    @Override
    public void loginSuccess() {
        Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void loginError(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showProgress() {
        dialog = ProgressDialog.show(this, " ", "加载中", true, true);
    }

    @Override
    public void hideProgress() {
        dialog.dismiss();
    }

    @Override
    public void showMessage(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

看上面的代码,很简洁,没有任何逻辑代码,这就是我们想要的效果,接下来就开始写Presenter层了,逻辑代码也是写到这一层。

/** * Created by yuankundong on 2016/03/03. */
public interface LoginPresenter {

    void actionLogin();
}

实现:

/** * Created by yuankundong on 2016/03/03. */
public class LoginPresenterImpl implements LoginPresenter, OnLoginListener {
    LoginView loginView;
    LoginModel loginModel;
    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginModel = new LoginModelImpl();

    }
    @Override
    public void actionLogin() {
        String username = loginView.getUsername();
        String password = loginView.getPassword();
        if (TextUtils.isEmpty(username)) {
            loginView.showMessage("用户名为空");
            return;
        }
        if (TextUtils.isEmpty(password)) {
            loginView.showMessage("密码为空");
            return;
        }
        loginView.showProgress();
        loginModel.toLogin(username,password,this);
    }

    @Override
    public void OnSuccess(UserBean userBean) {
        loginView.hideProgress();
        loginView.loginSuccess();
    }

    @Override
    public void OnError(int code, String msg) {
        loginView.hideProgress();
        loginView.loginError(msg);
    }
}

在LoginPresenterImpl 类中,我们实例了LoginView 和LoginModel 。登录的逻辑就是先判断用户名和密码是否为空,不为空再调用Model里的登录方法,进行登录。因为Model层把登录反馈传给OnLoginListener 这个回调接口,所有我们在这里实现这个接口,就能获取登录后的反馈。OnSuccess和OnError分别是登录成功和失败后,通知View层应该显示什么。

这样,我们用MVP写出来的登录就完成了,总体思想就是,Model层获取的数据交给Presenter处理,Presenter进行处理的过程中,会通知页面View层进行相应变化,View层本身不进行逻辑处理,MVP架构及时各有分工。
整体工程里面还有个别不涉及本文内容的我没有详述,源码地址,点击这里下载

你可能感兴趣的:(android,架构,mvp)