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

MVP模式简介

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

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

mvp架构设计与性能优化 mvp设计案例_ide


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

mvp架构设计与性能优化 mvp设计案例_加载数据_02

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

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

项目目录结构

mvp架构设计与性能优化 mvp设计案例_加载数据_03

结构很清晰,我对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模式完美的应用到我们的项目中了。

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