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

重拾Android之路(十七)MVP

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

关于MVP,MVVM,早就烂大街了,说出来可能都不要意思,在实际项目中,我没有将这两个内容使用的非常熟练,因为在项目中,一般都是一些比较小型的项目,真的用这些模式的地方并不多。但是最近几天在GitHub上面看到一个不错的共享程序–头条。感觉各方面都挺好的,所以就在闲暇的时间仔细研究了一下,发现了很多值得学习的地方,那么记录一下上面用到的技术,自己也要好好的学习,这是地址仿今日头条
今天来说一说MVP模式

概述

为什么使用MVP模式

可能有些人从一开始学习编程,到现在还没有使用过这些诸如MVP,MVVM的模式,大部分使用的是MVC,这是因为一般情况下,MVC的模式更容易理解,在代码量非常大的时候,如果贸然的改到MVP,是需要非常多的工作量的。但是,一个思想的提出肯定有他的过人之处,那么MVP的过人之处就是层次清晰。
举个例子在我们洗衣服的时候,我们肯定会将袜子和内衣分开来洗,因为如果一起洗的话,不卫生,那么我们可以认为,在需要洗袜子的时候,只关注如何洗袜子就可以了,而当我们洗内衣的时候,只需要关注内衣就可以,在某一个阶段只关注某一个方面。这样比较有条理。其实MVP模式也是一样,在我们程序员看来,如果能够很好的将数据操作,交互,用户界面完全的分离,那么我们在编写代码的时候,是非常轻松的,而且,如果某一个功能出现了问题,直接找到相应的模块即可。
MVP也有自己的缺点,这个缺点,我现在这里说一下,后面在实际使用中,会在演示,就是当程序慢慢增加的时候,代码量会超级多,文件也会更多。

逻辑思路图

这个图是我从网上找的,这里是原图和原文的地址android MVP框架搭建
重拾Android之路(十七)MVP
MVP模式分为三层:
Activity和Fragment视为View层,负责和用户的交互
Presenter为业务逻辑层,技能调用UI逻辑,又能请求数据,该层应该有纯Java代码实现,不应该涉及Android API
Model为数据层,包括数据请求,数据源操作等内容
这里View和Callback都是接口,两者的不同之处在于Callback是用于数据操作的接口,如数据返回等,View是一些跟UI界面相关的,如按钮点击事件的操作等。
说了这么多,其实都不如直接上代码来的实在,一个例子,模拟网络请求
先看效果,我做的GIF很难看,凑合吧
重拾Android之路(十七)MVP

Model层

model层主要用来模拟网络请求,这里使用handler的方式,延迟3秒钟,这里面有一个callback的对象,用来实现model和presenter的交互,就是这个callback会通知presenter中特定的方法,去执行相应的操作

public class MVPModel {

    /**
     * 获取网络数据
     *
     * @param param
     * @param callback
     */
    public static void getNetData(final String param, final BaseCallback callback) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (param) {
                    case "normal":
                        callback.onSuccess("请求数据成功");
                        break;
                    case "failure":
                        callback.onFailure("请求数据失败");
                        break;
                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }
        }, 3000);
    }

}

Presenter层

具体业务逻辑层,主要用来发送网络请求,并且会对请求的返回进行处理,并且会通知activity进行相应的UI改变。这里MVPCallback就是在Model中的那个Callback对象,Model中的数据进行操作之后,会触发相应的方法在Presenter中执行。而在Presenter中,我们生命了MVPView接口对象,这个对象就是用来实现Presenter层和Activity进行交互的接口对象。

public class MVPPersenter implements MVPCallback<String> {

    private MVPView mvpView;

    public MVPPersenter() {

    }

    /**
     * 情况:如果我们正在进行网络请求,而这时我们的Activity被销毁了,那么会出现空指针异常的情况,为了解决和避免这一情况的发生,需要使用绑定
     */
    public void attachView(MVPView mvpView) {
        this.mvpView = mvpView;
    }

    public void detachView() {
        this.mvpView = null;
    }

    public boolean isViewAttach() {
        return this.mvpView != null;
    }

    /**
     * 获取网络请求数据
     */
    public void getData(String param) {

        //打开进度条
        mvpView.showLoading();
        MVPModel.getNetData(param, this);

    }

    @Override
    public void onSuccess(String data) {
        if (isViewAttach()) {
            mvpView.showData(data);
        }
    }

    @Override
    public void onFailure(String msg) {
        if (isViewAttach()) {
            mvpView.showFailureMsg(msg);
        }
    }

    @Override
    public void onError() {
        if (isViewAttach()) {
            mvpView.showError();
        }
    }

    @Override
    public void onComplete() {
        if (isViewAttach()) {
            mvpView.hideLoading();
        }
    }
}

View层

这里我们直接使用的是Activity表示,这里我们需要让Activity实现MVPView接口和声明Presenter对象,这样,当presenter对象接收到相应的操作的时候,就通知Activity进行相应的UI操作

public class MainActivity extends AppCompatActivity implements MVPView{

    private Button button;
    private TextView textView;

    private MVPPersenter mvpPersenter;

    private ProgressDialog dialog;

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

        mvpPersenter = new MVPPersenter();
        mvpPersenter.attachView(this);

        button = findViewById(R.id.btn_1);
        textView = findViewById(R.id.msg);

        //初始化进度条
        dialog = new ProgressDialog(this);
        dialog.setCancelable(false);//不能自动关闭
        dialog.setMessage("正在加载");


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mvpPersenter.getData("normal");
            }
        });

    }

    @Override
    public void showLoading() {
        if(!dialog.isShowing()){
            dialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (dialog.isShowing()){
            dialog.dismiss();
        }
    }

    @Override
    public void showData(String data) {
        textView.setText(data);
    }

    @Override
    public void showFailureMsg(String msg) {
        textView.setText(msg);
    }

    @Override
    public void showError() {
        Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
        textView.setText("网络请求数据出现异常");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mvpPersenter.detachView();
    }
}

MVPView接口

这是一个接口,完成View和Presenter之间的数据交互

public class MainActivity extends AppCompatActivity implements MVPView{

    private Button button;
    private TextView textView;

    private MVPPersenter mvpPersenter;

    private ProgressDialog dialog;

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

        mvpPersenter = new MVPPersenter();
        mvpPersenter.attachView(this);

        button = findViewById(R.id.btn_1);
        textView = findViewById(R.id.msg);

        //初始化进度条
        dialog = new ProgressDialog(this);
        dialog.setCancelable(false);//不能自动关闭
        dialog.setMessage("正在加载");


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mvpPersenter.getData("normal");
            }
        });

    }

    @Override
    public void showLoading() {
        if(!dialog.isShowing()){
            dialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (dialog.isShowing()){
            dialog.dismiss();
        }
    }

    @Override
    public void showData(String data) {
        textView.setText(data);
    }

    @Override
    public void showFailureMsg(String msg) {
        textView.setText(msg);
    }

    @Override
    public void showError() {
        Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
        textView.setText("网络请求数据出现异常");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mvpPersenter.detachView();
    }
}

MVPCallback接口

实现Model和Presenter的交互

public interface MVPCallback<T> {

    /**
     * 请求成功
     * @param data
     */
    void onSuccess(T data);

    /**
     * 请求失败
     * @param msg
     */
    void onFailure(String msg);

    /**
     * 请求数据失败,一边表现在请求数据方式不对,网络权限失败,内存泄漏等原因无法连接到请求数据源
     */
    void onError();

    /**
     * 请求完成,不管请求是成功,失败,还是异常,都会执行完成操作
     */
    void onComplete();

}

总结

这就是最简单的一个关于MVP的例子,但是这里我们存在很多问题

  1. 框架存在漏洞
  2. 代码冗余
  3. 代码重构性差

我们需要对这个内容进行新一轮的修改,那么为了解决代码重构的问题,我们一般都是依赖于继承的方式解决,那么就是给我们需要的操作写一些父类

BaseCallback

通过泛型,让我们可以传递不同类型的数据

public interface BaseCallback<T> {

    void onSuccess(T data);

    void onFailure(String msg);

    void onError();

    void onComplete();

}

BasePresenter

有些时候在我们执行网络请求时,如果突然App异常退出,会导致数据请求一半,然后没有响应了。那么我们需要将View接口和Activity进行绑定

public class BasePresenter <V extends BaseView>{


    private V view;

    public void attachView(V view){
        this.view = view;
    }

    public void detachView(){
        this.view = null;
    }

    public boolean isAttachView(){
        return this.view != null;
    }

    public V getView(){
        return this.view;
    }

}

BaseView

View接口的基类

public interface BaseView {

    void showLoading();

    void hideLoading();

    void showToast(String msg);

    void showError();

    Context getContext();

}

View接口的子类

public interface SecondView extends BaseView {

    void showData(String data);

}

BaseActivity

在BaseActivity中我们需要实现BaseVIew接口,并且需要声明进度条

public abstract class BaseActivity extends AppCompatActivity implements BaseView {

    //声明一个加载进度狂
    private ProgressDialog dialog;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        dialog = new ProgressDialog(this);
        dialog.setCancelable(false);
    }

    @Override
    public void showLoading() {
        if (!dialog.isShowing()){
            dialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (dialog.isShowing()){
            dialog.dismiss();
        }
    }

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

    @Override
    public void showError() {
        showToast("请求失败");
    }

    @Override
    public Context getContext() {
        return BaseActivity.this;
    }
}

BasePresenter的子类

public class SecondPresenter extends BasePresenter<SecondView> {

    public void getData(String param) {
        if (!isAttachView()) {
            return;
        }
        getView().showLoading();

        MVPModel.getNetData(param, new BaseCallback<String>() {
            @Override
            public void onSuccess(String data) {
                getView().showData(data);
            }

            @Override
            public void onFailure(String msg) {
                getView().showToast(msg);
            }

            @Override
            public void onError() {
                getView().showError();
            }

            @Override
            public void onComplete() {
                getView().hideLoading();
            }
        });
    }

}

BaseActivity的子类

public class SecondActivity extends BaseActivity implements SecondView {

    private Button btn;
    private TextView textView;

    private SecondPresenter persenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = findViewById(R.id.btn_1);
        textView = findViewById(R.id.msg);

        persenter = new SecondPresenter();
        persenter.attachView(this);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                persenter.getData("normal");

            }
        });

    }

    @Override
    public void showData(String data) {
        textView.setText(data);
    }



    @Override
    protected void onDestroy() {
        super.onDestroy();
        persenter.detachView();
    }
}

Model

不需要改变

public class MVPModel {

    /**
     * 获取网络数据
     *
     * @param param
     * @param callback
     */
    public static void getNetData(final String param, final BaseCallback callback) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (param) {
                    case "normal":
                        callback.onSuccess("请求数据成功");
                        break;
                    case "failure":
                        callback.onFailure("请求数据失败");
                        break;
                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }
        }, 3000);
    }

}

MVPDemo例子
除了这个之外,我又写了一个关于登录的MVPdemo,也把代码贴出来,都是比较基础的内容登录功能的MVP实现

相关标签: Android MVP