MVP设计模式浅析、实战

来源:互联网 发布:python与量化投资 编辑:程序博客网 时间:2024/05/16 01:04

这篇文章介绍Android开发中MVP的概念以及实战MVP实例

MVP模式简介

  • View 对应于Activity,负责View的绘制以及与用户交互;
  • Model 依然是业务逻辑和实体模型;
  • Presenter 负责完成View于Model间的交互

图例,其实很简单,Presenter起到了连接View和Model的任务,这样我们的Activity就不会再处理业务相关的繁琐代码。
那么MVP的关键也就是如何让Presenter层来完成View和Model之间的交互。

这里写图片描述


接下来我们就从具体的实际案例中来深入学习MVP模式的设计和编码。

这里写图片描述

案例很简单,我们就实现了一个列表:

  1. 加载数据前显示模拟loading的Toast;
  2. 模拟方法子线程加载数据;
  3. 加载数据完成隐藏loading,将数据显示在列表中。

项目目录结构

这里写图片描述

结构很清晰,我对View层和Presenter层都做了一个基类的抽象,来降低我们代码的耦合度。


View层

BaseView
public interface BaseView {    void showLoading();    void hideLoading();}

这里只抽象了两个常用的试图操作的方法,你可以在这里增加更多的View操作的方法,比如showToast之类。

GirlView
public interface GirlView extends BaseView{    void setListItem(List<GirlEntity> data);    void showMessage(String message);}

继承了BaseView,不仅持有了BaseView里面的view操作方法,同时新增了自己列表页面的View方法,显示列表数据,以及点击Item显示Message信息。


Presenter

BasePresenter
public class BasePresenter<T> {    public T mView;    public void attachView(T view){        this.mView = view;    }    public void dettach(){        mView = null;    }}

这里在Presenter的基类里面我们使用了一个泛型T,因为我们不知道具体的Presenter层里面持有的到底是哪一个View,因此我们使用泛型来解决BasePresenter对View的持有。同时我们在BasePresenter中提供了两个方法,attacheView(View v)用来绑定Presenter和View,dettach()用来解绑Presenter和View层关系,这样我们可以实现有效的资源释放,避免出现OOM。

GirlPresenter
public class GirlPresenter extends BasePresenter<GirlView>{    private Handler mHandler;    public GirlPresenter() {        mHandler = new Handler(Looper.getMainLooper());    }    public void getGirls(){        mView.showLoading();        final List<GirlEntity> list = new ArrayList<>();        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(3*1000);                    for (int i=0; i < 10; i++){                        GirlEntity girlEntity = new GirlEntity();                        girlEntity.name = "赵丽颖"+i;                        girlEntity.imgRes = R.drawable.img;                        list.add(girlEntity);                    }                    mHandler.post(new Runnable() {                        @Override                        public void run() {                            mView.hideLoading();                            mView.setListItem(list);                        }                    });                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();    }}

这里我继承了BasePresenter,同时指定GirlPresenter持有了GirlView。然后我在GirlPresenter中实现了获取,绑定数据到View的操作,也就是实现了Model层和View层的交互。其实代码并不复杂,我才开始数据加载前用View层显示了loading,然后我在子线程处理了业务数据,处理完数据后我又切换至主线程完成了View层隐藏loading和数据显示。


Activity

Activity层主要是用来View层方法的实现以及Presenter层方法的初始化。

这里我同样抽象一个BaseActivity出来。

BaseActivity
public abstract class BaseActivity<V,T extends BasePresenter<V>> extends AppCompatActivity{    public T presenter;    /**     * 根据不同页面实现自己的P层     * @return     */    public abstract T createPresenter();    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        presenter = createPresenter();        //取的关联        presenter.attachView((V)this);    }    @Override    protected void onDestroy() {        super.onDestroy();        presenter.dettach();    }}

同样,我在BaseActivity中用泛型定义了一个Presenter,因为我们不确定具体的某一个Activity中究竟需要哪个Presenter。因为Presenter持有View的泛型对象,因此这里需要使用双层泛型的方法实现。然后我通过一个静态方法createPresenter()巧妙的实现了具体Activity中具体Presenter的初始化。之后我将Presenter跟View的处理关系同Activity的生命周期关联,这样就完美的实现了绑定和解绑操作。

GirActivity

接下来就是我们具体的某一个Activity里的操作了。

public class GirlActivity extends BaseActivity<GirlView,GirlPresenter> implements GirlView{    private ListView mListView;    @Override    public GirlPresenter createPresenter() {        return new GirlPresenter();    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mListView = (ListView) findViewById(R.id.listview);        presenter.getGirls();    }    @Override    public void showLoading() {        Toast.makeText(this,"加载数据中...",Toast.LENGTH_SHORT).show();    }    @Override    public void hideLoading() {        Toast.makeText(this,"数据加载完成...",Toast.LENGTH_SHORT).show();    }    @Override    public void setListItem(List<GirlEntity> data) {        GirlAdapter adapter = new GirlAdapter(data,this);        mListView.setAdapter(adapter);    }    @Override    public void showMessage(String message) {    }}

我们可以看到,Activity中的代码很简单也很清晰,我们初始化了GirlPresenter,这样GirlPresenter和GirlView就产生了关联,通过调用GirlPresenter中getGirls()方法,我们就可以获取列表数据,同时将View的操作回调到我们Activity实现的GirlView的方法中。


至此,MVP的使用就结束了。可能代码中涉及到一些基类的抽象,使得我们对MVP模式的理解略显复杂。但抽象是为了使我们简化代码,降低耦合度,相信这个工作是必须要做的。因此,建议大家自己动手,可以先不做抽象,单纯对某一个Activity进行某一个特定Presenter的实例化,让特定的Presenter持有特定的View,这样更方便大家的理解。当然理解完还是建议大家根据自己的理解和需要,对View和Presenter进行抽象,再对Activity进行抽象,这样就可以将MVP模式完美的应用到我们的项目中了。

如果对你有帮助,欢迎大家留言讨论,点赞关注!