MVP基本了解及使用和封装

来源:互联网 发布:锐捷交换机ip mac绑定 编辑:程序博客网 时间:2024/06/10 01:37

http://blog.csdn.net/u014769864/article/details/69668740

转地址载:

想了解一个新事物我会按以下步骤来:
1、它是什么;
2、它有什么用(出现的理由),且有什么优缺点
而这里对mvp阐述分为以下步骤:
一、MVP出现原因;
二、MVP简单例子;
三、MVP总结
       1、mvp是什么;
       2、mvp有什么用?
       3、mvp有那些缺点?。
四、MVP简单封装(简化MVP使用,把公共部分抽离出来,也就是所谓的封装,类似BaseActivity那样的基类)。
另外本人水平有限,有什么疑问的地方可以提出来相互讨论。

MVP出现原因


Android场景举例:

功能需求:点击一个按钮,然后在点击监听里做一些操作(什么操作都可以,下面会具体说是什么操作)。
我们使用android studio来实现这个功能,步骤:
1、在res/layout下创建一个xml文件然后添加按钮控件;
2、在activity下设置该按钮的监听事件,然后在监听听里面做一些你想要的操作。

问题起因:
我们可以想象一下,如果我在上面那个按钮的点击事件里做访问网络,然后获取json数据,这个操作是不是和activity或xml(UI)联系在一起的呢?如果访问网络获取数据出现问题是不是就会牵扯到activity(UI)呢?我们再想象一下,如果我那个activity有20个类似按钮监听里的逻辑操作,都和activity牵扯在一起,会有什么后果?Bom,Bom,Bom。整个activity(UI)除了臃肿,还有就是一堆逻辑傻操作都和activity牵扯在一起,出事也牵扯到activity(UI),这给用户造成的影响会是骂一句然后你又屁颠屁颠的加班维护得累死累活的呢?


怎么解决问题?

这时候mvp出现了,然后体现了它的作用和优点,mvp就是决解上面问题而出现的吧,我也没查过,不过使用mvp架构思想,就可以很好的解决上面提出的问题。解决什么问题呢?后面我们就知道能解决那些问题了;但首先我们得知道mvp每一层的意思。



基本了解


这里借用这位兄弟的一段话和图(http://www.jianshu.com/p/bd99bda72912)

MVP是Model-View-Presenter,它们的关系如下图:



[java] view plain copy
print?
  1. Model:业务逻辑和实体模型,用来操作实际的数据,包含Bean和Model的抽象接口来降低耦合。  
  2. View:就是Android中的视图,需要建立一个View的抽象接口View Interface。通过实现View的接口来实现View与Presenter的交互,从而降低耦合。对应于Activity,负责View的绘制与用户交互;  
  3. Presenter:View和Model的中间枢纽,处理和用户交互的逻辑。  


MVC就是Model-View-Controller,它们的关系如下图:

[java] view plain copy
print?
  1. (数据模型)Model:数据的封装和保存,业务逻辑和实体模型  
  2. (视图)View:视图界面,对应于布局文件  
  3. (控制器)Controller:业务逻辑,对应于Activity、Fragment等  

它们的逻辑为:View传送指令到Controller,Controller完成业务逻辑后,改变Model的状态,Model将新的数据发送到View,这就是MVC模式的处理逻辑。

MVP和MVC的对比:

 MVP架构:

      View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。

      Presenter与View的交互是通过接口来进行的。

      通常ViewPresenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。

 MVC架构:

      View可以与Model直接交互。

      Controller是基于行为的,并且可以被多个View共享。

      可以负责决定显示哪个View



了解了mvp每一层的意思和逻辑后,我们现在就以上面按钮例子以MVP的思想架构来实现,看看有什么不同。

mvp每一层意思一定要搞清楚!mvp每一层意思一定要搞清楚!mvp每一层意思一定要搞清楚!


MVP简单例子


例子功能:一个按钮,点击该按钮实现网络请求,返回网络数据。
例子具体显示功能:
1、点击按钮就去请求网络数据;
2、显示progressbar(提示网络数据请求中);
3、请求结束,返回数据(有成功和失败,这里为了简单,只写成功);
4、隐藏progressbar(表示请求结束)。

下面是该例子的工程结构



MVP简单例子步骤

1、首先我们在res/layout下建立一个xml,在里面添加按钮和文本还有progressbar控件,这里就不贴代码了;

2、下面是MainActivity代码:

[java] view plain copy
print?
  1. package com.mvptest;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v7.app.AppCompatActivity;  
  5. import android.view.View;  
  6. import android.widget.Button;  
  7. import android.widget.ProgressBar;  
  8. import android.widget.TextView;  
  9. import android.widget.Toast;  
  10.   
  11. import com.mvptest.mvp.presenter.MainPresenter;  
  12. import com.mvptest.mvp.view.IMainView;  
  13.   
  14. public class MainActivity extends AppCompatActivity implements IMainView {  
  15.   
  16.     private TextView mTextView;  
  17.     private Button mButton;  
  18.     private ProgressBar mProgressBar;  
  19.     private MainPresenter mainPresenter = new MainPresenter(this);  
  20.   
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         setContentView(R.layout.activity_main);  
  25.   
  26.         initView();  
  27.         initListener();  
  28.     }  
  29.   
  30.     private void initView() {  
  31.         mTextView = (TextView) findViewById(R.id.tv_data);  
  32.         mButton = (Button) findViewById(R.id.bt_data);  
  33.         mProgressBar = (ProgressBar) findViewById(R.id.pb);  
  34.     }  
  35.   
  36.     private void initListener() {  
  37.         mButton.setOnClickListener(new View.OnClickListener() {  
  38.             @Override  
  39.             public void onClick(View v) {  
  40.                 //网络请求加载数据  
  41.                 mainPresenter.loadData();  
  42.             }  
  43.         });  
  44.     }  
  45.   
  46.       
  47.     //======================下面是继承mvp/view包下的IMainView需要实现的方法=========================  
  48.     @Override  
  49.     public void showDialog() {  
  50.         mProgressBar.setVisibility(View.VISIBLE);  
  51.     }  
  52.   
  53.     @Override  
  54.     public void hideDialog() {  
  55.         mProgressBar.setVisibility(View.GONE);  
  56.     }  
  57.   
  58.     @Override  
  59.     public void showData(String data) {  
  60.         mTextView.setText(data);  
  61.         Toast.makeText(this"请求数据成功", Toast.LENGTH_SHORT).show();  
  62.     }  
  63. }  


我们看到上面的MianActivity继承mvp/view包下的IMainView,然后实现了某些方法,这些方法并没有做什么业务逻辑操作,而是只做了UI的显示。另外MainActivity里面的
private MainPresenter mainPresenter = new MainPresenter(this);和mainPresenter.loadData()现在不需要知道里面做了什么,到这里只要知道当我们
点击按钮时调用mainPresenter.loadData()是请求网络数据操作就行,然后我们继承IMainView实现了需要重写的方法,在重写的方法里做更新UI的操作就行
了。
小结:
我们在前面就说了mvp的v,也就是view层责任就是显示IU,其他事都不会去做,那我们在这里就给mvp中的v,也就是view层给个具体一些的概念,它指什么?它
指项目中res/layout下的xml文件和activity或fragment(也就是UI了)。
3、接下来是大家之前比较关心mvp/view包下IMainView的代码:
[java] view plain copy
print?
  1. package com.mvptest.mvp.view;  
  2.   
  3. /** 
  4.  * Created by tu2460 on 2017/4/8. 
  5.  */  
  6.   
  7. public interface IMainView {  
  8.     void showDialog();//显示progressbar进度  
  9.     void hideDialog();//隐藏progressbar进度  
  10.     void showData(String data);//显示网络请求数据  
  11. }  
IMainView就是一个接口,里面3个方法,作用是把网络请求数据结果回掉(返回)给MainActivity(UI)更新。怎么个回掉呢,后面会说。
小结:
到这里我们可以知道mvp中的v,也就是view层指什么了,也就是和直接操作UI有关的东西,我们这里具体指MainActivity、xml布局、IMainView,
但是我并没有把MainActivity放到mvp/view包下,只是为了更加清晰的说明
4、mvp/presenter包下MainPresenter代码:
上面说完了mvp中的v,也就是view层,现在到mvp中的p,也就是presenter层了,而且前面我们也说了presenter的职责是什么,它负责view和model中
间的协调,但是不互相影响。presenter也只是负责协调,其他逻辑或者显示UI都不会去做,只做自己的事情,给view和model充当一座桥。
[java] view plain copy
print?
  1. package com.mvptest.mvp.presenter;  
  2.   
  3. import com.mvptest.mvp.model.ILoadDataListener;  
  4. import com.mvptest.mvp.model.MainModle;  
  5. import com.mvptest.mvp.view.IMainView;  
  6.   
  7. /** 
  8.  * Created by tu2460 on 2017/4/8. 
  9.  */  
  10.   
  11. public class MainPresenter {  
  12.     private IMainView mIMainView;  
  13.     private MainModle mMainModle;  
  14.   
  15.     public MainPresenter(IMainView mIMainView) {  
  16.         this.mIMainView = mIMainView;  
  17.         this.mMainModle = new MainModle();  
  18.     }  
  19.   
  20.     /** 
  21.      * 加载网络数据 
  22.      */  
  23.     public void loadData() {  
  24.         //回掉显示进度的progressbar接口  
  25.         mIMainView.showDialog();  
  26.         //请求数据  
  27.         mMainModle.getData(new ILoadDataListener() {  
  28.             @Override  
  29.             public void loadSuccess(String data) {  
  30.                 //回掉隐藏进度的progressbar接口  
  31.                 mIMainView.hideDialog();  
  32.                 //回掉显示返回请求数据的接口  
  33.                 mIMainView.showData(data);  
  34.             }  
  35.         });  
  36.     }  
  37. }  
我们看到上面MainPresenter全是都是接口的调用(MainModle后面会说到),并没有做业务逻辑或UI处理等其他事情,因为我们一开头就说了mvp每一层都有自己的职责,不会多做其他不属于自己的事情。
小结:
我们已经了解了mvp中view层,现在到presenter层,开头我们说了它作用就是连接view和modle的桥梁,在上面MainPresenter代码中也看到,只做链接
IMainView和MainModle(各种接口回掉)的事情,这里presenter层具体指MainPresenter这个类。
5、接下来下面是mvp/modle包下MainModle代码:
我们前面说完了mvp的view层、presenter层,现在到modle层了。
前面我们也说了modle层的职责,它的职责是什么?当然是处理业务逻辑等操作咯。
[java] view plain copy
print?
  1. package com.mvptest.mvp.model;  
  2.   
  3. import android.os.Handler;  
  4.   
  5. /** 
  6.  * Created by tu2460 on 2017/4/8. 
  7.  */  
  8.   
  9. public class MainModle {  
  10.     private Handler mHandler = new Handler();  
  11.   
  12.     public void getData(final ILoadDataListener loadDataListener){  
  13.         //在这里去获取网络数据,为了简化这里就使用handler模拟了  
  14.         mHandler.postDelayed(new Runnable() {  
  15.             @Override  
  16.             public void run() {  
  17.                 //回掉请求成功的接口  
  18.               loadDataListener.loadSuccess("网络获取的数据");  
  19.             }  
  20.         },3000);  
  21.     }  
  22.   
  23. }  

MainModle里的loadDataListener.loadSuccess("网络获取的数据")是一个网络请求监听接口,案例是写在里面合理些的,但是这里也是为了看得清楚些,就

把他单独写出来了,放在了mvp/modle下ILoadDataListener。

6、下面是mvp/model包下ILoadDataListener代码:

[java] view plain copy
print?
  1. package com.mvptest.mvp.model;  
  2.   
  3. /** 
  4.  * Created by tu2460 on 2017/4/8. 
  5.  */  
  6.   
  7. public interface ILoadDataListener {  
  8.     void loadSuccess(String data);  
  9. }  

ok,我们看到上面MainModle做了什么呢?做了网络请求数据的业务操作,然后把结果通过ILoadDataListener也就是网络请求监听的接口处理。

小结:

我们前面知道了mvp中的view层,然后知道了presenter层,现在也知道了modle层,modle层的作用就是做一些业务逻辑处理等,而这里的modle层指:

项目mvp/modle包下的MainModle和ILoadDataListener(这个接口也可以写到MainModle里)。


MVP总结

MVP是什么鬼?

mvp是一种思想架构,一种开发模式,上面的demo就是使用了mvp这种思想完成的。
具体点:上面整个demo就是以mvp思想架构搭建的,项目mvp包下分成了3块
1、model 块;
2、presenter 块;
3、view  块;
很清楚,每一块职责也很明确,并且不相互影响,这就是mvp,它只是一种想法,模式罢了。


MVP有什么用?

现在我们回到一开头就提到问题,前言里的起因:(不知道怎么设置标签让大家点击“起因:”就可以跳到上面大哭)

而我们demo以mvp模式来完成的
1、view层只负责UI更新,显示;
2、modle层只负责业务逻辑的处理;
3、presenter层,各种接口调用,协调view和modle层。

此刻在想想前面的前言里谈到起因里的问题,现在还会有那样的问题吗?
mvp作用是什么?清晰明确,还有呢?易于维护、扩展,还有呢?解耦,还有吗?这个还得你自己去使用...




MVP缺点

上面demo中我们也看到了,一个简单的点击按钮请求网络对比普通写法多了那么多类(上面是为了更加简单介绍才没写那么多的~~),没错缺点是你需要考虑的东西变多了,需要写的类也变多了,这里还得说明一下上面举得例子,上面例子是为了更加简单解释MVP,并没有很标准以MVP去实现demo的功能,如果那样去做就会多出2个接口类,看起来会更加模糊。除了类爆炸,还有吗?这你得自己去使用MVP,并且在去了解MVC、MVVM等其他思想对比后才能看得更加清楚了。


小总结:

从上面的每个小节我们知道了mvp,然后具体点也知道每一层指什么东西了(例如demo中),这里我们在整理下上面demo的思路:

1、我们点击按钮;

2、按钮响应网络请求事件,也就是使用presenter层mainPresenter.loadData()方法;

3、方法里面先调用显示progressbar加载进度接口,然后在调用Modle层里的方法,也就是
mMainModle.getData(ILoadDataListenerloadDataListener);

4、Modle层MainModle里的getData方法执行网络请求操作,把成功数据通过Presenter层,也就是MainPresenter回掉给MainActivity,也就是
MainActivity继承了mvp/view包下的IMainView重写的方法,数据会回掉到重写的方法里。

顺序是:View层响应操作->通过Presenter层通知->Modle层执行业务逻辑,然后业务逻辑处理完->通过Presenter层把数据或结果回掉给->View层用于更新UI。

MVP简单封装

我们知道BaseActivity、BaseFragment、Base...这里自然也要封装一下,这里只是对上面的demo进行封装示范,具体项目封装根据情况。
我们在原来的demo下建立一个base包用来放公用的一些东西。

1、mvp/view包下的IMainView里有showDialog()和hideDialog()方法,这两个方法大多数都会用到,我们把公共部分抽取出来,放到名字为MVPIBaseView里,代码如下:
[java] view plain copy
print?
  1. package com.mvptest.base;  
  2.   
  3. /** 
  4.  * Created by tu2460 on 2017/4/9. 
  5.  */  
  6.   
  7. public interface MVPIBaseView {  
  8.     void showDialog();  
  9.     void hideDialog();  
  10. }  


2、mvp/presenter包下的MainPresenter构造方法里每个都要绑定IMainView这样的view,我们也抽取出来,另外还有一个事情要说,就是presenter会持有view,比如网络请求,这时如果返回退出了Activity,后台异步的动作不会立即停止,这里就会有内存泄漏的隐患,所以会在presenter中加入一个销毁view的方法。

MVPBasePresenter代码如下:

[java] view plain copy
print?
  1. package com.mvptest.base;  
  2.   
  3. import java.lang.ref.Reference;  
  4. import java.lang.ref.WeakReference;  
  5.   
  6. /** 
  7.  * Created by tu2460 on 2017/4/9. 
  8.  */  
  9.   
  10. public class MVPBasePresenter<T> {  
  11.   
  12.     protected Reference<T> mIView;//View接口类型的弱引用  
  13.   
  14.     public void attachView(T iView){  
  15.   
  16.         mIView=new WeakReference<T>(iView);//建立关系  
  17.   
  18.     }  
  19.   
  20.     protected T getView(){  
  21.   
  22.         return mIView.get();  
  23.     }  
  24.   
  25.     public boolean isViewAttached(){  
  26.   
  27.         return mIView!=null&&mIView.get()!=null;  
  28.     }  
  29.   
  30.   
  31.     public void detachView(){  
  32.   
  33.         if (mIView!=null){  
  34.   
  35.             mIView.clear();  
  36.             mIView=null;  
  37.         }  
  38.   
  39.     }  
  40.   
  41. }  



3、mvp的view层和presenter都抽取了公共部分,而modle层就这个demo来看,没有什么公用的部分就不做抽取了,但我们还得写一个MVPBaseActivity类,用于绑定和销毁mvp/view包下IMainView这样的,传递进去的上下文,因为这些步骤也是重复的。
MVPBaseActivity代码:

[java] view plain copy
print?
  1. package com.mvptest.base;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.annotation.Nullable;  
  5. import android.support.v7.app.AppCompatActivity;  
  6.   
  7. /** 
  8.  * Created by tu2460 on 2017/4/9. 
  9.  */  
  10.   
  11. public abstract class MVPBaseActivity<V, T extends MVPBasePresenter<V>> extends AppCompatActivity {  
  12.   
  13.     protected T mPresenter;  
  14.   
  15.     @Override  
  16.     protected void onCreate(@Nullable Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         mPresenter = createPresenter();//创建presenter  
  19.         mPresenter.attachView((V) this);  
  20.     }  
  21.   
  22.   
  23.     @Override  
  24.     protected void onDestroy() {  
  25.         super.onDestroy();  
  26.         mPresenter.detachView();  
  27.     }  
  28.   
  29.   
  30.     protected abstract T createPresenter();  
  31. }  


4、最后我们看看我们原来的MainActivity有什么变化,代码如下:

[java] view plain copy
print?
  1. package com.mvptest;  
  2.   
  3. import android.os.Bundle;  
  4. import android.view.View;  
  5. import android.widget.Button;  
  6. import android.widget.ProgressBar;  
  7. import android.widget.TextView;  
  8. import android.widget.Toast;  
  9.   
  10. import com.mvptest.base.MVPBaseActivity;  
  11. import com.mvptest.mvp.presenter.MainPresenter;  
  12. import com.mvptest.mvp.view.IMainView;  
  13.   
  14. public class MainActivity extends MVPBaseActivity<IMainView,MainPresenter> implements IMainView {  
  15.   
  16.     private TextView mTextView;  
  17.     private Button mButton;  
  18.     private ProgressBar mProgressBar;  
  19.   
  20.     @Override  
  21.     protected void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.activity_main);  
  24.   
  25.         initView();  
  26.         initListener();  
  27.     }  
  28.   
  29.     @Override  
  30.     protected MainPresenter createPresenter() {  
  31.         return new MainPresenter();  
  32.     }  
  33.   
  34.   
  35.     private void initView() {  
  36.         mTextView = (TextView) findViewById(R.id.tv_data);  
  37.         mButton = (Button) findViewById(R.id.bt_data);  
  38.         mProgressBar = (ProgressBar) findViewById(R.id.pb);  
  39.     }  
  40.   
  41.     private void initListener() {  
  42.         mButton.setOnClickListener(new View.OnClickListener() {  
  43.             @Override  
  44.             public void onClick(View v) {  
  45.                 //网络请求加载数据  
  46.                 mPresenter.loadData();  
  47.             }  
  48.         });  
  49.     }  
  50.   
  51.   
  52.     //======================下面是继承mvp/view包下的IMainView需要实现的方法=========================  
  53.     @Override  
  54.     public void showDialog() {  
  55.         mProgressBar.setVisibility(View.VISIBLE);  
  56.     }  
  57.   
  58.     @Override  
  59.     public void hideDialog() {  
  60.         mProgressBar.setVisibility(View.GONE);  
  61.     }  
  62.   
  63.     @Override  
  64.     public void showData(String data) {  
  65.         mTextView.setText(data);  
  66.         Toast.makeText(this"请求数据成功", Toast.LENGTH_SHORT).show();  
  67.     }  
  68. }  


上面和原来比有什么变化呢? private MainPresenter mainPresenter = new MainPresenter(this);换成了在重写的createPresenter()方法里了,另外按钮点击事件里的对象使用的是MVPBaseActivity里的mPresenter了。



总结:
mvp只是开发多了的人而提出的一种开发模式,方法罢了,具体怎么用还是得看自己。下面给出我认为比较标准的MVP基本写法demo,当你熟练后,可以参照上面的封装对MVP进行相应的封装。

demo下载

原创粉丝点击