Android MVP实战

本文通过实例详细介绍了MVP(Model-View-Presenter)设计模式的原理及应用,强调了MVP模式在解耦方面的作用,适合希望深入理解MVP模式的开发者阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前些天,在公司实习的测试妹纸回学校答辩了,要我给她做个毕业设计,说要求不高,看看界面就行。

七搞八搞给做了个,但是项目一开始并不是用mvp写的,因为那时感觉对mvp的理解还不是很深刻。前两天有空,就把它改成了mvp风格的样子,所以也就有了这篇博客。

网上也有很多文章跟demo,初看时始终不得其解,后来在将那个项目改成mvp的过程中才开始有了拨云见月的感觉,这篇博客也将尽可能用通俗易懂的语言来帮助更多像我一样的初学者更好的理解什么是Mvp。

纸上得来终觉浅,绝知此事要躬行。建议大家还是先自己动手,写一个登录注册的demo,然后把它改成mvp的风格,在改的过程中,你也就能慢慢理解里面的思想了。

M

Model,即数据对象跟业务逻辑。在学校学Java EE的时候,项目里总是有很多的DTO、DAO对象,如果把我们项目中的XXBean看做DTO的话,那么这里的Model就可以看成是Java EE里的DAO了。

假设这里有一个UserBean的话,那么与之对应我们就应该对应一个UserModel,而设计模式讲究:

依赖接口编程,而不是依赖细节编程

所以我们一般先定义一个IUserModel接口,然后UserModel来实现它。

IUserMode.java

package com.baobeikeji.shower.bean.imodel;

import com.baobeikeji.shower.bean.UserBean;

/**
 * Created by wpj on 16/5/10上午10:26.
 * 处理数据、业务逻辑
 */
public interface IUserMode {

    UserBean loadUser(String id);

    UserBean loadUser(String id, String name, String password, String schoolName, String question, String answer);
}

UserModel.java

package com.baobeikeji.shower.bean.model;

import android.text.TextUtils;

import com.baobeikeji.shower.bean.UserBean;
import com.baobeikeji.shower.cache.CacheManager;
import com.baobeikeji.shower.bean.imodel.IUserMode;
import com.baobeikeji.shower.bean.imodel.ISchoolMode;
import com.baobeikeji.shower.bean.imodel.IValidateMode;
import com.google.gson.Gson;

/**
 * Created by wpj on 16/5/10上午10:34.
 */
public class UserModel implements IUserMode {
    private Gson mGson;
    private ISchoolMode mSchoolMode;
    private IValidateMode mValidateMode;

    public UserModel() {
        mGson = new Gson();
        mSchoolMode = new ScoolMode();
        mValidateMode = new ValidateMode();
    }

    @Override
    public UserBean loadUser(String id) {
        UserBean userBean = new UserBean(id);
        //CacheManager是自己定义的一个缓存工具类
        String result = CacheManager.getManager().onReadFromCache(userBean);
        if (TextUtils.isEmpty(result)) {
            return null;
        }
        return mGson.fromJson(result, UserBean.class);
    }

    @Override
    public UserBean loadUser(String id, String name, String password, String schoolName, String question, String
            answer) {

        //可以完成 校验参数合法性,设置默认值等操作
        UserBean userBean = new UserBean();
        userBean.id = id;
        userBean.name = name;
        userBean.password = password;
        userBean.scool = mSchoolMode.loadSchool(schoolName);
        userBean.addValidateBean(mValidateMode.loadValidateBean(question, answer));
        return userBean;
    }
}

IUserModel封装一些对UserBean各种增删改查的操作,而一开始里面有哪些方法我们是不知道的,所以后面根据实际需要在里面添加。

V

View,即展示给用户的界面。在以往mvc风格开发的app中,view就是我们的activity,然后在大家日常的开发中,习以为常的把各种网络请求、数据处理等操作都放在activity、fragment中,使其动辄成百上千行,这使我们后面的维护任务相当艰巨。

设计模式讲究单一职责原则,既然activity作为view,那就只应该处理好自己的本分职责,而不应该既负责显示UI又要负责处理数据。

在Mvp中,数据处理部分交由Model来实现,而View只负责UI显示。那Model处理数据的时候需要显示UI怎么办呢?

假设有一个LoginActivity,与之对应我们应当新建一个ILoginView接口,并在其中声明各种与UI交互的方法,比如显隐dialog、get/set文本框等等。

ILoginView.java

package com.baobeikeji.shower.login;

import com.baobeikeji.shower.bean.UserBean;

/**
 * Created by wpj on 16/5/10 上午 10:26.
 * <p>
 */
public interface ILoginView {

    String getUsername();

    String getPassword();

    void loginSuccessed(UserBean userBean);

    void showToast(String msg);
}

那谁来实现这些方法呢?那就是我们的LoginActivity。

LoginActivity.java

package com.baobeikeji.shower.login;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

import com.baobeikeji.shower.MainActivity;
import com.baobeikeji.shower.R;
import com.baobeikeji.shower.app.BaseActivity;
import com.baobeikeji.shower.bean.UserBean;
import com.baobeikeji.shower.login.forgetpassword.ForgetPasswordActivityMvp;
import com.baobeikeji.shower.login.register.RegisterActivityMvp;

public class LoginActivityMvp extends BaseActivity implements ILoginView {

    private EditText mUsernameEt, mPasswordEt;
    private LoginPresenter mLoginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    }

    @Override
    public void onInitViews() {
        mUsernameEt = (EditText) findViewById(R.id.login_username_et);
        mPasswordEt = (EditText) findViewById(R.id.login_password_et);
    }

    @Override
    public void onInitListeners() {

        findViewById(R.id.login_regist_tv).setOnClickListener(this);
        findViewById(R.id.login_forget_password_tv).setOnClickListener(this);
        findViewById(R.id.login_submit_btn).setOnClickListener(this);
    }

    @Override
    public void onInitData() {

        mLoginPresenter = new LoginPresenter(this);
    }

    @Override
    public void onClick(View view) {

        Intent intent;
        switch (view.getId()) {
            case R.id.login_regist_tv:

                intent = new Intent(LoginActivityMvp.this, RegisterActivityMvp.class);
                startActivity(intent);
                break;
            case R.id.login_forget_password_tv:

                intent = new Intent(LoginActivityMvp.this, ForgetPasswordActivityMvp.class);
                startActivity(intent);
                break;
            case R.id.login_submit_btn:

                if (!checkOk(mUsernameEt, mPasswordEt)) {
                    t("用户名或者密码为空");
                    return;
                }
                mLoginPresenter.login();
                break;
            default:
                break;
        }
    }

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

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

    @Override
    public void loginSuccessed(UserBean userBean) {
        Intent intent = new Intent(LoginActivityMvp.this, MainActivity.class);
        intent.putExtra(MainActivity.USER, userBean);
        startActivity(intent);
        finish();
    }

    @Override
    public void showToast(String msg) {
        t(msg);
    }
}

其实Model处理数据的时候怎么显示UI的这个问题,本身就问的不太合理,在mvp里面,Model和View是完全隔离的两个部分:

  • Model处理数据时并不关心View该怎么显示
  • 而View层显示时也并不关心Model的处理结果。

真正把Model和View结合起来的就是Presenter。

P

我理解的是调度中心、控制中心,有道翻译说的是任命者,感觉意思也差不多。

经过上面两步,处理数据的Model有了,跟UI交互的View有了,怎么使他们能够合理的协调工作呢?

假设现在在LoginActivity,用户填完账号、密码之后,点击登录按钮,程序校验账号、密码,并根据不同的校验结果让UI做出不同的处理:跳转到主页,或者是给出一个错误的提示。

整个过程大概分为两步:

  • 用户点击登录后,程序校验账号密码:View -> Presenter -> Model
  • 程序返回校验结果,UI做出不同处理:Model -> Presenter -> View

大概流程图如下:

这里写图片描述

LoginPresenter.java

package com.baobeikeji.shower.login;

import com.baobeikeji.shower.bean.UserBean;
import com.baobeikeji.shower.bean.imodel.IUserMode;
import com.baobeikeji.shower.bean.model.UserModel;

/**
 */
public class LoginPresenter {

    private IUserMode mLoginModel;
    private ILoginView mLoginView;

    public LoginPresenter(ILoginView loginView) {
        mLoginView = loginView;
        mLoginModel = new UserModel();
    }

    public void login() {
        String username = mLoginView.getUsername();
        String password = mLoginView.getPassword();

        UserBean userBean = mLoginModel.loadUser(username);
        if (null == userBean) {

            mLoginView.showToast("用户不存在");
        } else if (!userBean.isValidateSuccessed(password)) {

            mLoginView.showToast("答案验证失败");
        } else {

            mLoginView.showToast("验证成功");
            mLoginView.loginSuccessed(userBean);
        }
    }
}

看到这里,是不是很明朗了。

代码示例

Talk is cheap , show me your code。装逼谁不会,拿代码来。

这是整个项目结构,初学mvp时,网上到处都是那个登录注册的demo,我这demo比那个逼格高多了,不仅有登录注册,还有找回密码。(哇,逼格好高啊。。。)

里面有的activity有两个,比如LoginActivity、LoginActivityMvp,前面说了,这个项目一开始不是mvp写的,后来改成mvp了,所以之前的也还保留着,大家可以对照着看。

这里写图片描述

来看看效果图:

登录

注册

找回密码

总结

之前一直纠结是学Mvp还是直接上Mvvm,毕竟习惯了mvc的开发模式,突然接触mvp有种摸不着头脑的感觉,现在想想,感觉其实学习下mvp的这种设计思想也是挺好的。

至于mvc跟mvp孰优孰劣,因为接触的不长,不过也感觉到mvp在解耦上面还是挺好的,这么多年一直讨论设计模式,最终要的一点不就是解耦吗?

毕竟也是菜鸟,有些东西可能理解的不是很完善,希望大家一起来交流。我也会对mvp mvvm继续保持关注的。

源码下载

另外分享一个基于Material Design和MVP的新闻客户端,这是它github的链接

推荐阅读

浅析如何高效的使用MVP

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值