Android——最接近实战的MVP模式

前言

说起MVP大家现在也肯定不陌生了,当项目越来越复杂,参与的开发人员越多的时候,MVP的优势就体现出来了,他会让代码的逻辑特别的清晰,在维护代码或者我们要在一个Activity中加入新功能的时候,特别的方便。其实Android本身的开发可以说是一个MVC模式,Activity兼顾View和Controller,既绑定xml的布局文件,又要在Activity中处理一些逻辑代码,其实这样不太好,耦合度太高,我们把View和Controller抽离出来变成了View和Presenter,这便是MVP模式。

MVP简单介绍

其实网上对它的介绍已经很多了,我也不说了,一张图足以:

Model:比如在Model层中处理网络请求或者一些逻辑,尤其是很多可以复用的业务逻辑,多个模块都会用到的东西写在Model中是最好的了,打个比方,比如说一个项目中多次用到了上传图片,今天又新加了一个模块,此模块又需要上传图片,那么我们就不必在Presenter中再重复的写一次上传图片的功能了,直接用写好的model,传入本地路径,回调中进行操作即可,非常省事。但是如果这一个地方的逻辑只有这里会用到,其实可以考虑将原本写在Model的东西直接写在Presenter中,这样也比较省事。注意Model并不是JavaBean,要区分开来

View:就是Activity,只处理简单的UI操作,设置文本值,或者弹个框啦什么的

Presenter:负责处理Model中的数据,并且写一些逻辑的代码

实践

我自己写了一个MVP,假想了一些场景和需求,然后用最接近实战的方式演示一下:

那么我们要实现两个功能:

  1. 发送网络请求得到一个名字,显示在Activity中
  2. 发送网络请求得到一篇文章,显示在Activity中

这个也是我随机想的一些需求吧,实际的需求可能比较复杂,我们就用这几个非常简单的例子来实现MVP:

1、首先先来网络请求基本的操作我发送网络请求是用Retorfit,那么写个接口,url都是虚拟的,这里模拟一下

public interface GetMyInfoApi {
    /**
     * 获取我的名字
     * @param id
     * @return
     */
    @GET("exempt/getName")
    Call<NameBean> getMyName(@Query("id") String id);

    /**
     * 获取我的文章
     * @param id
     * @return
     */
    @GET("exempt/getArticle")
    Call<ArticleBean> getMyArticle(@Query("id") String id);
}

2、接受回调的bean

public class BaseResponseBean implements Serializable {

    public String status;

    public String errmsg;

    /**
     * 假设接口返回的state字段值为0证明请求成功,否则请求失败则存在错误信息errmsg
     * @return
     */
    public boolean isSucceed(){
        return TextUtils.equals(status,"0");
    }
}


public class NameBean extends BaseResponseBean{

    public String name;
}


public class ArticleBean extends BaseResponseBean{

    public String article;
}

3、Model层,将回调传给Presenter,让Presenter去处理Model中的数据,记得对bean要进行判空

public class MainModelImpl implements IMainModel{

    GetMyInfoApi mApi = RequestUtils.createService(GetMyInfoApi.class);

    /**
     * 得到我的名字
     *
     * @param id
     * @param callback
     */
    @Override
    public void getMyName(String id, final ICallback<NameBean> callback) {
        mApi.getMyName(id).enqueue(new Callback<NameBean>() {
            @Override
            public void onResponse(Call<NameBean> call, Response<NameBean> response) {
                NameBean bean = response.body();
                if (bean == null) {
                    return;
                }
                if (bean.isSucceed()) {
                    callback.onSucceed(bean);
                } else {
                    callback.onError(bean.errmsg);
                }
            }

            @Override
            public void onFailure(Call<NameBean> call, Throwable t) {
                callback.onError("网络连接不可用!");
            }
        });
    }

    /**
     * 得到我的文章
     *
     * @param id
     * @param callback
     */
    @Override
    public void getMyArticle(String id, final ICallback<ArticleBean> callback) {
        mApi.getMyArticle(id).enqueue(new Callback<ArticleBean>() {
            @Override
            public void onResponse(Call<ArticleBean> call, Response<ArticleBean> response) {
                ArticleBean bean = response.body();
                if (bean == null) {
                    return;
                }
                if (bean.isSucceed()) {
                    callback.onSucceed(bean);
                } else {
                    callback.onError(bean.errmsg);
                }
            }

            @Override
            public void onFailure(Call<ArticleBean> call, Throwable t) {
                callback.onError("网络连接不可用!");
            }
        });
    }
}

4、接下来就是写接口啦,官方推荐的写法是将View和Presenter这两个接口都放到Contract这个类当中,可以看到我们的任务已经非常的清晰啦,这里要继承BasePresenter和BaseView也是之前的项目这么写,在BaseView当中有个setPresenter的方法,将Presenter传入Activity中。其实也可以简单一点,Presenter和View都不用继承基类,然后在Activity中直接new个Presenter出来,然后直接传入this,Presenter的构造方法中得到mView,这样也是可以的,和大家说一下,以免看不懂为何要继承BasePresenter和BaseView,就是一个习惯问题吧,怎么整都是可以的,只要把View和Presenter整的他俩能联系上了就行。

interface MainContract {
    
    interface Presenter extends BasePresenter{
        /**
         * 发送网络请求得到name
         */
        void getMyName(String id);
        /**
         * 发送网络请求得到文章内容
         */
        void getMyArticle(String id);
        
    }
    
    interface View extends BaseView<Presenter>{
        /**
         * 显示名称
         */
        void showMyName(String name);
        /**
         * 显示文章
         */
        void showMyArticle(String article);
        /**
         * 显示toast
         */
        void showToast(String msg);

        /**
         * Activity是否被销毁
         */
        boolean isViewFinishing();
    }
}

5、再就是Activity了,让它接上View接口,new一个Presenter出来传入this,调用mPresenter的getMyName()和getMyArticle方法,id是随便写的假的哈哈,点击事件呢调用mPresenter的addGroup()方法,那么就会发送网络请求,然后在Presenter中又会调用View中的show方法来显示数据。

public class MainActivity extends AppCompatActivity implements MainContract.View{
    private TextView tvName;
    private TextView tvArticle;

    private MainContract.Presenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new MainPresenter(this);
        initView();
    }
    public void initView(){
        tvName = (TextView) findViewById(R.id.tv_name);
        tvArticle = (TextView) findViewById(R.id.tv_article);
        mPresenter.getMyName("123");
        mPresenter.getMyArticle("123");
    }

    @Override
    public void setPresenter(MainContract.Presenter presenter) {
        mPresenter = presenter;
    }

    /**
     * 显示名称
     *
     * @param name
     */
    @Override
    public void showMyName(String name) {
        tvName.setText(name);
    }

    /**
     * 显示文章
     *
     * @param article
     */
    @Override
    public void showMyArticle(String article) {
        tvArticle.setText(article);
    }

    /**
     * 显示toast
     *
     * @param msg
     */
    @Override
    public void showToast(String msg) {
        Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
    }

    /**
     * Activity是否被销毁
     */
    @Override
    public boolean isViewFinishing() {
        return isFinishing();
    }
}

6、最后写Presenter,得到mView,并且实例化Model,这就是我们说的Presenter与View和Model联系,View和Model是不能直接联系的,得到数据后处理数据,直接在回调中调用mView.show()方法,把数据以参数的形式传过去就OK,我这里因为是假的接口,肯定会执行onFailure()方法,在onFailure中模拟个假数据,这样运行项目就可以显示数据啦。

public class MainPresenter implements MainContract.Presenter{
    
    private MainContract.View mView;

    public MainPresenter(MainContract.View mView) {
        this.mView = mView;
        this.mView.setPresenter(this);
        
    }
    /**
     * 发送网络请求得到name
     *
     * @param id
     */
    @Override
    public void getMyName(String id) {
        new MainModelImpl().getMyName(id, new ICallback<NameBean>() {
            @Override
            public void onSucceed(NameBean data) {
                if (mView.isViewFinishing()) {
                    return;
                }
                mView.showMyName(data.name);
            }

            @Override
            public void onError(String msg) {
                if (mView.isViewFinishing()) {
                    return;
                }
                mView.showToast(msg);
            }
        });

    }

    /**
     * 发送网络请求得到文章内容
     *
     * @param id
     */
    @Override
    public void getMyArticle(String id) {
        new MainModelImpl().getMyArticle(id, new ICallback<ArticleBean>() {
            @Override
            public void onSucceed(ArticleBean data) {
                if (mView.isViewFinishing()) {
                    return;
                }
                mView.showMyArticle(data.article);
            }

            @Override
            public void onError(String msg) {
                if (mView.isViewFinishing()) {
                    return;
                }
                mView.showToast(msg);

            }
        });

    }

    @Override
    public void start() {

    }
}

可能有的人会觉得这调过来调过去的,不是多此一举吗,其实这也是我的例子写的比较简单,当你的项目很大的时候,用MVP会感觉非常的舒适,你会感觉逻辑非常的清晰,尤其当某一块业务逻辑会多次使用的时候,在Model层的东西可以复用,是非常爽的。当然,如果有那种特殊的情况,比如一个Activity特别简单,甚至连网络都不用请求,那就不用写MVP了。我们一般目录的放法是这样的:Model层独立放一个包,Activity、Contract、Presenter是放在一个包下,可以看Demo

Demo地址:https://github.com/pengboboer/MvpTest

总结

这就是一个最接近实战的一个MVP模式,自己也是重新想了一个也亲自写了一下,加深记忆,网上很多人写的非常的复杂,Model层也不知道写了些什么,模拟也没模拟一个真实的网络请求,有的看起来乱七八糟的,不好理解。如果没用过Retrofit的同学提前看一下Retrofit,那么希望能帮助到一些初学者,大家一起共勉!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值