欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-12 20:05:26
...

前言

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

MVP简单介绍

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

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

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

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

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

实践

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

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

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

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

这个也是我随机想的一些需求吧,实际的需求可能比较复杂,我们就用这几个非常简单的例子来实现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是放在一个包下:

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

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

总结

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