前言

新年新气象,Blin祝大家在新的一年中事业一帆风顺!
好了,年是过完了,不知道小伙伴们是否也都已经到了工作岗位,开始新的一年的征程了呢。废话不多说,我们这次主要是以聊一聊为
主,简单讲一讲在我们的Android开发中被大家普遍接受的MVP开发,上干货!

简介

对于MVP,全称是Model View Presenter,相比对这东西都有点耳熟能详的感觉有木有,现在很多项目都是用这个开发模式进行公司
或个性项目的开发。其实我在第一次了解MVP的时候,感觉就是东西不错,但是烦。那么MVP到底是什么呢?其实也很简单,就是由我们
以往一直使用的MVC演化而来,如果你做过公司的大型项目,想必你也无时无刻不在抱怨,一个项目主页面的Activity少
则数千行,多则上万行。当然此篇博文更多的是作为一篇日记一样以及本人的一些想法,如果有什么问题的话也请大牛们指正,一起相互学习。

和MVC的区别

这一点的话必须要搞清楚,为什么会有MVP,归根结底就是为了弥补一些MVC的缺点。
它们的基本思想肯定是有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模
式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)
来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。

区别如下图:(引用hongyang大神博文的的图片)

android 简单mvp实例 安卓开发mvp_android 简单mvp实例


个人总结MVP的话其实用一句话来说明就可以了:大型项目,多人开发MVP必定是未来的趋势。如果是单人或者两个人开发的

话,个人觉得你用MVC也是很不错的,毕竟MVP开发成本太高,至少从类上来讲,那多出的类的个数差不多是一倍之差啊。

那说了这么多没用的,我们也来总结一下MVP的优点吧:

1、模型与视图完全分离,我们可以修改视图而不影响模型

2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部

3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型

的变化频繁。

4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)

Login MVP Demo

还有其他的一些概念性的东西也就不多说了,我们直接看Demo然后来真正的熟悉一下到底MVP开发模式是如何应用在实际的项

目中的,我这里以一个简单的一个登陆系统为例子来说一下吧。(本来想用自己个人项目的【比比账号通】的登录接口,但是为了

简单化,主要是了解MVP,所以我们就模拟一下吧)

首先来看一下我们要实现的效果:

android 简单mvp实例 安卓开发mvp_MVC_02


这个功能如果大家用MVC来写,那肯定是分分钟的事情,但是如果是用MVP,可能就像我第一次自己写的的时候还真有点不好

写,下图是小项目的结构:

android 简单mvp实例 安卓开发mvp_android 简单mvp实例_03


那么我们怎么来运用MVP呢,顺着我自己的思路一步一步来,首先既然是一个登陆系统,那么必然存在用户这个bean类,我们先

把bean类写好,如下:

package com.mic.blin.loginmvpdemo.bean;

/**
 * Created by zhaocheng on 2016/2/18.
 */
public class User {
    private String telephone;
    private String password;

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

(一)Modle

最简单的Bean写好,那么我们就要开始摸索这个MVP中的Modlel.
这个小项目就一个小功能,那就是登陆,那么是谁来用这个功能呢,很显然,不用多讲,新建IUser。如下:

package com.mic.blin.loginmvpdemo.model;

/**
 * Created by zhaocheng on 2016/2/18.
 */
public interface IUser {
    void login(String telephone,String password,OnLoginListener loginListener);
}

当然我这是直接写完后再把代码贴上来,给大家理一遍的,登录功能嘛,肯定要账号密码的,那么为什么还要一个借口呢,也很简
单,就是为了处理登录后,回调接口,用来处理登录后要处理的事情,对吧。应该不难理解的。
然后创建UserModel,去实现上面的接口,代码如下:

package com.mic.blin.loginmvpdemo.model;

import com.mic.blin.loginmvpdemo.bean.User;

/**
 * Created by zhaocheng on 2016/2/22.
 */
public class UserModel implements IUser {
    @Override
    public void login(final String telephone, final String password, final OnLoginListener loginListener) {
        //这边我们需要模拟一个耗时操作
        new Thread()
        {
            @Override
            public void run()
            {
                try
                {
                    Thread.sleep(1000);
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                //然后这边模拟登录成功
                if ("blin".equals(telephone) && "000000".equals(password)){
                    User user = new User();
                    user.setTelephone(telephone);
                    user.setPassword(password);
                    loginListener.loginSuccess(user);
                }else{
                    loginListener.loginFailed();
                }
            }
        }.start();
    }

}

上面的就是主要的Model了,我这边就是模拟了一下登录请求,用子线程睡眠模拟。不要吐槽,我们的重点不是在这里(。・∀・)ノ゙。
然后忘记了一个,上面的接口我们还没有定义呢,当然实际开发你肯定会先写这个回调接口的,不然看上IUser报错也是不爽,尤其是我这种有强迫症的人。

package com.mic.blin.loginmvpdemo.model;

import com.mic.blin.loginmvpdemo.bean.User;

/**
 * Created by zhaocheng on 2016/2/18.
 */
public interface OnLoginListener {
    void loginFailed();
    void loginSuccess(User user);
}

Model其实就是这么简单的,和以往的写法也没什么区别是吧。

(二)View

接下来我们来看看View,其实呢没说白了,我们做这么多就是为了解耦,那么和View相关的事件有什么呢,以往可能直接在
Activity中写的和View有关的事件在MVP中都会通过接口,然后Activity再去实现。所以,刚开始不能理解,或者摸不着头脑的,
可以先按照以往的写法,然后再进去解耦。当然这样的方法肯定是违背了我们用MVP的初衷的。
我也就直接贴出干活了,大家自己看下也应该明白,就是把一些以往直接写在Activity中的和View有关的操作通过接口一一分开封装起来啦。
package com.mic.blin.loginmvpdemo.view;

import com.mic.blin.loginmvpdemo.bean.User;

/**
 * Created by zhaocheng on 2016/2/22.
 */
public interface IUserLogin {
    String getTelephone();
    String getPassword();
    void showLoading();
    void hideLoading();
    void toLoginFailedView();
    void toLoginSuccessView(User user);
}

然后就是用Activity去实现啊这些方法了呗。

package com.mic.blin.loginmvpdemo.view;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.mic.blin.loginmvpdemo.R;
import com.mic.blin.loginmvpdemo.bean.User;
import com.mic.blin.loginmvpdemo.presenter.UserLoginPresenter;

public class MainActivity extends AppCompatActivity implements IUserLogin{

    private Button btn_login;
    private EditText et_account,et_password;
    private UserLoginPresenter userLoginPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        initEvent();
    }
    private void init(){
        btn_login = (Button) findViewById(R.id.btn_login);
        et_account = (EditText) findViewById(R.id.et_account);
        et_password = (EditText) findViewById(R.id.et_password);
        userLoginPresenter = new UserLoginPresenter(this);
    }
    private void initEvent(){
        btn_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                userLoginPresenter.login();
            }
        });
    }

    @Override
    public String getTelephone() {
        return et_account.getText().toString();
    }

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

    @Override
    public void showLoading() {
        btn_login.setText("正在登陆。。。");
    }

    @Override
    public void hideLoading() {
        btn_login.setText("登录");
    }

    @Override
    public void toLoginFailedView() {
        Toast.makeText(this,"账号或密码错误!",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void toLoginSuccessView(User user) {
        Toast.makeText(this,"欢迎"+user.getTelephone()+"正在跳转。。。",Toast.LENGTH_SHORT).show();
    }
}

当然我们的Presenter还没写,我就直接把整个Avtivity贴出来了。。。有点不妥。我们从上面的点击事件中可以看到,点击以后就
是通过userLoginPresenter去调用登录方法了。所以真的在Activity中的逻辑你都看不到了,全都的业务都有Presenter接手。

(三)Presenter

最后就是我们的Presenter了,他的出现就是为了帮助我们处理Model和View之间的数据交互啦,基本上所有的业务也都在这里了。

package com.mic.blin.loginmvpdemo.presenter;

import android.os.Handler;

import com.mic.blin.loginmvpdemo.bean.User;
import com.mic.blin.loginmvpdemo.model.OnLoginListener;
import com.mic.blin.loginmvpdemo.model.UserModel;
import com.mic.blin.loginmvpdemo.view.IUserLogin;

/**
 * Created by zhaocheng on 2016/2/22.
 */
public class UserLoginPresenter {
    private UserModel userModel;
    private IUserLogin userView;
    public  User user;
    private Handler handler = new Handler();
    public UserLoginPresenter(IUserLogin userView){
        userModel = new UserModel();
        this.userView = userView;
        this.user = new User();
    }
    public void login(){
        userView.showLoading();
        userModel.login(userView.getTelephone().toString(), userView.getPassword().toString(), new OnLoginListener() {
            @Override
            public void loginFailed() {
                //注意这里,我们是模拟了延迟操作,而且是在子线程,所以这里要调用改变UI的操作必须回到主线程。
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        userView.toLoginFailedView();
                        userView.hideLoading();
                    }
                });
            }

            @Override
            public void loginSuccess(final User user) {
                //同上
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        userView.toLoginSuccessView(user);
                        userView.hideLoading();
                    }
                });
            }
        });
    }
}

业务就是一个登录,对的,那就来一个登录的方法接口呗。用我们最简单的逻辑去想一下,当点击登录以后,然后调用登录方法,
那么在Presenter中肯定有我们在Activity中实现的接口对象,还有就是Model对象。还有就是那个回调接口直接在这里被初始化
传入,然后处理登录后处理相应的操作,就是因为接口对象的存在,才使得Activity中的方法在Presenter中被轻松的调用到。

总的来说,对于刚入门的,可能这几个接口就把你搞晕了,的确是有点绕,比直接使用我们习惯的MVC的确要夺走一点路子。但是这些东西也是值得的,试想,本来一个页面中的东西可能用MVC开发,等到大家都签入的时候,各种冲突,然而如果是用MVP
开发的话,大家同时使用的就那个Activity,就是每个人多了一些业务接口呗。

大家刚接触的绝对不要慌,这个东西其实昨天和我朋友两个人也聊了很多,的确发现很多年以避免的问题,尤其是类爆炸,或者是当业务逻辑比较复杂时,自己都会搞晕,或者说为了写一个简单的功能浪费了2倍的时间等等,但是作为一个Android的 不管以后是不是会成为主流,但是相比还是要了解一下的,对吧。

PS

好啦,这是2016的第一篇,今天又是元宵,祝大家节日快乐哈~

代码下载地址:

Android MVP 下载