MVP模式从入门到精通
首先附上自己写的一个MVP的demo,这是一个很标准的MVP,Github地址如下:
https://github.com/SilasGao/MVPDemo
首先MVP 是从经典的MVC架构演变而来,那我们是不是要先说下何为MVC模式?
系统C/S(Client/Server)三层架构模型:
1)视图层(View):一般采用XML文件对应用的界面进行描述,使用的时候可以直接引入,极为方便,可以的大大缩短开发时间,也可以使用JavaScript+HTML等的方式作为View层,当然这里需要进行Java和JavaScript之间的通信,幸运的是,Android提供了非常方便的通信实现。业务逻辑层(BLL):它的关注点主要集中在业务规则的制定、业务流程的实现等与业务需求有关的系统设计,也即是说它是与系统所应对的领域(Domain)逻辑有关,很多时候,也将业务逻辑层称为领域层。
2)控制层(Controller):Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理。
3)模型层(Model):对数据库的操作、以及其他和数据有关的的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。
三层结构架构三层间的交互及作用如下图所示:
传统的MVC模式是很不错,我们也很熟悉,毕竟用了这么多年了。(突然想到之前去一家公司面试他问个HelloWord的Android项目是不是MVC,我说不是,他说回答错误,阿西吧!)
在Android项目上你会发现Activity的责任太重,什么东西都要放在Activity中,最终导致了Activity太过臃肿。虽然能抽的都抽出来了,但是会发现代码还是很多,试想下上千行代码还没有注释,能不晕?即使是自己写的,过些日子去看也有些晕晕的吧?
尤其代码敲完,一个月后需求又改了,从600、700行代码中找到要修改的地方也是要一点功夫的。
为了给Activity减轻压力,这时候MVP出现了!
MVP有什么好处,为什么要用MVP呢?
网上搜下一大堆MVP的各种好处,本人总结下主要有以下几点:
- 代码解耦
- 结构清晰
- 可复用
- 扩展性高
- 方便进行单元测试
- BuyBooActivity是我们的Activity
- BaseActivity是Activity的基类
- BasePresenter是Presenter的基类
- BuyBookBean是我们的bean,也就是传说中的实体类,几个成员变量,自动生成一堆get、set方法的那个类
- IBuyBookView也就是BuyBooActivity的接口
- BuyBookPresenter也就是这个Actvity的Presenter
- IBuyBookPresenter是BuyBookPresenter的接口
- BuyBookModel是这个Activity的数据层
- IBuyBookModel是BuyBookModel的接口
- BuyBookAdapter是BuyBooActivity里面ListView的适配器
- ValueCallBack,是一个通用的回调接口
- public interface IBuyBookContract
- {
- interface IBuyBookView
- {
- void showToast(String msg);
- void refreshAdapter();
- void onEmpty();
- }
- interface IBuyBookPresenter
- {
- List<BuyBookBean> getAdapterData();
- }
- }
下面开始贴代码讲解
先来讲View IBuyBookView
这里主要是包含了Activity的一系列关于UI操作,然后用我们的Activity是实现,这样Presenter就可以调用了。
- public interface IBuyBookView {
- /**
- * 提示toast
- */
- void showToast(String msg);
- /**
- * 刷新adapter
- */
- void refreshAdapter();
- void onEmpty();
- }
接下来讲Model IBuyBookModel BuyBookModel
主要是写了几个方法供BuyBookModel去实现
- public interface IBuyBookModel {
- /**
- * 获取模拟数据
- */
- void getTestData(ValueCallBack<List<BuyBookBean>> callBack);
- /**
- * 返回本地adapter数据
- * @return
- */
- List<BuyBookBean> getAdapterData();
- }
- public class BuyBookModel implements IBuyBookModel {
- private List<BuyBookBean> listData;
- public BuyBookModel() {
- this.listData = new ArrayList<>();
- }
- @Override
- public void getTestData(final ValueCallBack<List<BuyBookBean>> callBack) {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- List<BuyBookBean> list = new ArrayList<>();
- list.add(new BuyBookBean("赵云", 1, "09-27 09:11"));
- list.add(new BuyBookBean("赵云、隔壁老王、小王、典韦、貂蝉、林芳、曹操、刘备、关羽、黄忠、张飞、诸葛孔明", 10, "09-27 09:11"));
- list.add(new BuyBookBean("黄忠、孙权、大乔", 50, "09-27 09:11"));
- list.add(new BuyBookBean("大乔、小乔、貂蝉、孙尚香", 300, "09-27 09:11"));
- Random rd = new Random();
- int N = rd.nextInt(10);
- if (N > 5) {
- callBack.onSuccess(list);
- } else {
- callBack.onFail("拒绝请求");
- }
- }
- }, 1000);
- }
- @Override
- public List<BuyBookBean> getAdapterData() {
- return listData;
- }
- }
- public interface ValueCallBack<T> {
- void onSuccess(T t);
- void onFail(String code);
- }
接下来讲Presenter BasePresenter IBuyBookPresenterBuyBookPresenter
这个是所有Presenter的基类,里面有个initData()方法,基本每个Presenter都要处理网络请求吧,所以我就弄了这么一个基类,至于为什么是抽象的而不是接口,这是因为抽象方便点吧,如果我们往抽象类中添加新的方法(该方法不是抽象的),可以给他提供默认的实现,而且不要修改现有的代码,但是如果是接口的话,就要修改现有的代码了。指不定我们以后要往这里加什么呢。
这里为什么用了个泛型?为了让人一看这个Presenter就知道这对应着哪个Activity,实际上这可以不加的,但是我觉得加上去更好。便于后来人也便于自己以后再来修改这个类。
- public abstract class BasePresenter<T extends BaseActivity> {
- abstract void initData();
- }
这里主要写了个方法,以供BuyBookPresenter实现
- public interface IBuyBookPresenter {
- List<BuyBookBean> getAdapterData();
- }
这里首先实现现了IBuyBookPresenter继承了BasePresenter,然后重写一些方法。这里的构造方法是重点,在构造方法中,我们需要传入一个IBookView,实际上我们的Activity已经实现IBookView了,所以这里实际上传的是具体的Activity,也就是this就行了。然后model我们可以直接new出来用,这里就new了。
在initData中我们是进行了具体的网络请求,网络请求我们是不是要弹一个Dialog出来,直接在这mView.loading();调用就行了。然后请求成功onSuccess()里面让Dialog消失,提醒适配器刷新。失败的话onFail()里面提示Dialog消失,然后ListView设置失败页面什么的。
- public class BuyBookPresenter extends BasePresenter<BuyBookActivity> implements IBuyBookPresenter {
- private IBuyBookView mView;
- private IBuyBookModel mModel;
- public BuyBookPresenter(IBuyBookView iBuyBookView) {
- this.mView = iBuyBookView;
- this.mModel = new BuyBookModel();
- }
- @Override
- public List<BuyBookBean> getAdapterData() {
- return mModel.getAdapterData();
- }
- @Override
- public void initData() {
- //在这里弹出loading
- mModel.getTestData(new ValueCallBack<List<BuyBookBean>>() {
- @Override
- public void onSuccess(List<BuyBookBean> buyBookBeen) {
- //在这里取消loading
- //简单数据操作可以在presenter里完成
- mModel.getAdapterData().addAll(buyBookBeen);
- mView.refreshAdapter();
- }
- @Override
- public void onFail(String code) {
- //在这里取消loading
- mView.showToast(code);
- mView.onEmpty();
- }
- });
- }
- }
接下来讲我们的Activity BuyBookActivity BaseActivity
这是我们Acticity的基类,可以看到这里有个泛型,注意了,前方高能。这个泛型还必须继承BasePresenter,这个首先为了让人一看到这个Activity就知道对应着那个Presenter;其次最重要的就是为了下面那个成员变量basepresenter,我们写一个抽象的方法要求返回泛型T,而这个泛型T又继承了BasePresenter,那么我们就得到了具体Presenter的成员变量,可以直接用这个成员变量来调用Presenter中的方法了。
- public abstract class BaseActivity<T extends BasePresenter> extends Activity {
- protected T basepresenter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(getLayout());
- initView();
- basepresenter = initPresent();
- onPrepare();
- }
- abstract T initPresent();
- abstract int getLayout();
- abstract void initView();
- abstract void onPrepare();
- }
这个就是最终具体的Activity了,可以看到这里都没什么逻辑,基本都是一些重写的方法。
- public class BuyBookActivity extends BaseActivity<BuyBookPresenter> implements IBuyBookView
- {
- private ListView mListView;
- private BuyBookAdapter mAdapter;
- @Override
- BuyBookPresenter initPresent()
- {
- return new BuyBookPresenter(this);
- }
- @Override
- int getLayout()
- {
- return R.layout.activity_main;
- }
- @Override
- void initView()
- {
- mListView = (ListView) findViewById(R.id.listview);
- }
- @Override
- void onPrepare()
- {
- mAdapter = new BuyBookAdapter(this, basepresenter.getAdapterData());
- mListView.setAdapter(mAdapter);
- basepresenter.initData();
- }
- @Override
- public void showToast(String msg)
- {
- Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
- }
- @Override
- public void refreshAdapter()
- {
- mAdapter.notifyDataSetChanged();
- }
- public void onEmpty()
- {
- mListView.setEmptyView(null);
- }
- }
最后虽然MVP模式有许多好处,但是有一个致命的缺点就是类太多,本来一个类最多变成了7个类,最少变成6个类(使用Contract协议类)。所以并不是所有的页面都要用MVP模式的,很简单的页面就没必要了,浪费时间是不是。
为什么MVP模式利于单元测试?