Android MVP经验谈

欢迎转载,转载时请注明出处和作者
作者:kerwin
原文地址:
http://www.jianshu.com/p/284990eebf0c

****MVP->Model-View-Presenter****,意在解决业务逻辑与视觉层的解耦问题。关于MVC与MVP的区别相关资料很多,这里不再累述,相关传送门:
MVP模式(百度百科)

Android MVP经验谈_第1张图片

从上图看出,Presenter是作为Model与View层交互的连接器存在。将上图转化为代码实现的官方实例:Google官方architecture项目。

MVP模式类图

Android MVP经验谈_第2张图片

类图解析
核心的3个接口:BaseUI、BasePresenter、XContract。

****BasePresenter****,业务实现层的基础接口,所有Presenter的超父类,
方法:start(Bundle bundle),Presenter层的数据初始化,Bundle为常规页面数据传递的基本容器。

****BaseUI****,视图层的基础接口,所有UI实现类的超父类
方法:setPresenter(T presenter),用于关联UI与Presenter,主要用在Fragment与Presenter的绑定。

****XContract****,主要是为了将具体的UI和Presenter集合到一个文件中,方便集中查阅管理。

实践
需求:X页面包含3个需求。1、计算总成绩a+b,2、获取X的姓名和性别,3、展示总成绩和X的信息。

1)首先按照需求写出对应的Model类

public class XModle {

/**
 * @param a a课程分数
 * @param b b课程分数
 * @return  总分数
 */
public int sum(int a, int b) {
    return a + b;
}

/**
 * 假装这里是从服务器获取的数据
 * @param callback
 */
public void getX(Callback callback) {
    callback.onResult("kerwin", "boy");
}

public interface Callback {
    void onResult(String name, String gender);
}
}

2)创建XContract类

public interface XContract {

interface UI extends BaseUI {
  
}

interface Presenter extends BasePresenter {

}
}

3)根据业务需求添加UI和Presenter接口的方法

public interface XContract {
    /**
     * A课程得分的传递的参数名
     */
    String PARAM_INT_A = "a";
    /**
     * B课程得分的传递的参数名
     */
    String PARAM_INT_B = "b";

    interface UI extends BaseUI {

        /**
         * 通知UI,Sum已经计算出来,可以刷新对应的UI了
         */
        void refreshSumResult();

        /**
         * 通知UI,X的信息已经获取到,可以刷新X了。
         */
        void refreshX();

        /**
         * 显示loading弹窗
         * @param msg
         */
        void showLoading(String msg);

        /**
         * 关闭loading弹窗
         */
        void dismissLoading();

    }

    interface Presenter extends BasePresenter {

        /**
         * 获取X的姓名
         * @return
         */
        String getXName();

        /**
         * 获取X的性别
         * @return
         */
        String getXGender();

        /**
         * 获取总成绩
         * @return
         */
        String getSumScore();
    }

}

4)实现XPresenter类

public class XPresenter implements XContract.Presenter {

    private XContract.UI ui;

    private XModle modle;

    private int a, b;

    private int sum = 0;

    private String xName, xGender;

    public XPresenter(XContract.UI ui) {
        this.ui = ui;
        this.modle = new XModle();
    }

    @Override
    public void start(Bundle bundle) {
        a = bundle.getInt(XContract.PARAM_INT_A, 0);
        b = bundle.getInt(XContract.PARAM_INT_B, 0);
        start();
    }

    private void start() {
        sum = modle.sum(a, b);
        ui.refreshSumResult();
        ui.showLoading("正在获取X的信息");
        modle.getX(new XModle.Callback() {
            @Override
            public void onResult(String name, String gender) {
                ui.dismissLoading();
                xName = name;
                xGender = gender;
                ui.refreshX();
            }
        });
    }

    @Override
    public String getXName() {
        return "姓名:"+xName;
    }

    @Override
    public String getXGender() {
        return "性别:"+xGender;
    }

    @Override
    public String getSumScore() {
        return "总成绩"+sum;
    }
}

到这里,可以看到,我们Activity还没有开始写,但是我们的业务流程已经完成了,这就是MVP的魅力。至于页面长什么样子并不需要业务逻辑层去关注。现在我们理一下整个需求完成的程序流图。

****程序流图****

Android MVP经验谈_第3张图片
计算总分数
Android MVP经验谈_第4张图片
获取X的信息

看过程序流图后结合之前的代码,可能会问:为什么UI里面没有setXName方法,为什么Presenter里面有getXName方法。为什么Presenter从XModel里面获取到Sum返回值后不是直接调用ui.setSum(sum),而是ui.refreshSumResult()?

个人实践中发现,UI层是随时可能会变化的,但是Presenter基本不变,因为接口基本不变。所以,我推荐的MVP实践方式是,主动的在UI中从Presenter获取数据,而不是在Presenter中主动的修改UI信息,Presenter完成从Model获取数据的职责后仅需通知UI已经完成数据获取,UI想如何显示或显示什么自己决定。

****下面继续完善UI部分的实现代码****

5)新增layout文件activity_x.xml




    

    

    


6)新增XActivity类

public class XActivity extends Activity implements XContract.UI {

    private TextView tv_x_name;

    private TextView tv_x_gender;

    private TextView tv_sum;

    private XContract.Presenter presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_x);
        initView();
        initPresenter();
    }

    private void initView() {
        tv_sum = (TextView) findViewById(R.id.tv_sum);
        tv_x_name = (TextView) findViewById(R.id.tv_x_name);
        tv_x_gender = (TextView) findViewById(R.id.tv_x_gender);
    }

    private void initPresenter() {
        presenter = new XPresenter(this);
        presenter.start(getIntent().getExtras());
    }

    @Override
    public void setPresenter(XContract.Presenter presenter) {
        // 如果UI实现是一个Fragment,这里的代码是需要的,是Activity的这里留空即可
        this.presenter = presenter;
    }

    @Override
    public void refreshSumResult() {
        tv_sum.setText(presenter.getSumScore() + "");
    }

    @Override
    public void refreshX() {
        tv_x_name.setText(presenter.getXName());
        tv_x_gender.setText(presenter.getXGender());
    }

    @Override
    public void showLoading(String msg) {
        //显示Dialog弹窗
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void dismissLoading() {
        //关闭Dialog弹窗
    }
}

至此,所有的需求全部完成了。

****说在最后****
MVP每一层都有自己的职责,请尽量做到各司其职。
几点实践建议:
1、View层不包含任何Model层的代码以及引用
2、Presenter作为连接器,尽量不要直接调用UI层的具体UI更新的方法,仅需通知UI层自己去刷新某一个细分模块(就像上面的refreshX)

示例源码传送门

你可能感兴趣的:(Android MVP经验谈)