前言
想了解一个新事物我会按以下步骤来:
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下设置该按钮的监听事件,然后在监听听里面做一些你想要的操作。
问题起因(假设1):
我们可以想象一下,如果我在上面那个按钮的点击事件里做访问网络,然后获取json数据,这个操作是不是和activity或xml(UI)联系在一起的呢?如果访问网络获取数据出现问题是不是就会牵扯到activity(UI)呢?我们再想象一下,如果我那个activity有20个类似按钮监听里的逻辑操作,都和activity牵扯在一起,会有什么后果?Bom,Bom,Bom。整个activity(UI)除了臃肿,还有就是一堆逻辑傻操作都和activity牵扯在一起,出事也牵扯到activity(UI),如果你遇到一个Activity里的业务逻辑代码就能有一万多行(没遇到和做过的兄弟可能没这体会),当你来维护和迭代功能的时候,就应该能体会到一种MMP的感受了。这里得引用一句好的应用应该是“高内聚低耦合”这句话。
怎么解决问题?
这时候mvp出现了,然后体现了它的作用和优点,mvp就是决解上面问题而出现的吧,我也没查过,不过使用mvp架构思想,就可以很好的解决开发中存在的某些问题。解决什么问题呢?后面我们就知道能解决那些问题了;但首先我们得知道mvp每一层的意思。
基本了解
这里借用这位兄弟的一段话和图(http://www.jianshu.com/p/bd99bda72912)
MVP是Model-View-Presenter,它们的关系如下图:
Model:业务逻辑和实体模型,用来操作实际的数据,包含Bean和Model的抽象接口来降低耦合。
View:就是Android中的视图,需要建立一个View的抽象接口View Interface。
通过实现View的接口来实现View与Presenter的交互,从而降低耦合。
对应于Activity,负责View的绘制与用户交互;
Presenter:View和Model的中间枢纽,处理和用户交互的逻辑。
MVC就是Model-View-Controller,它们的关系如下图:
(数据模型)Model:数据的封装和保存,业务逻辑和实体模型
(视图)View:视图界面,对应于布局文件
(控制器)Controller:业务逻辑,对应于Activity、Fragment等
它们的逻辑为:View传送指令到Controller,Controller完成业务逻辑后,改变Model的状态,Model将新的数据发送到View,这就是MVC模式的处理逻辑。
MVP和MVC的对比:
MVP架构:
View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。
Presenter与View的交互是通过接口来进行的。
通常View与Presenter是一对一的,但复杂的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代码:
package com.mvptest;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.mvptest.mvp.presenter.MainPresenter;
import com.mvptest.mvp.view.IMainView;
public class MainActivity extends AppCompatActivity implements IMainView {
private TextView mTextView;
private Button mButton;
private ProgressBar mProgressBar;
private MainPresenter mainPresenter = new MainPresenter(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}
private void initView() {
mTextView = (TextView) findViewById(R.id.tv_data);
mButton = (Button) findViewById(R.id.bt_data);
mProgressBar = (ProgressBar) findViewById(R.id.pb);
}
private void initListener() {
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//网络请求加载数据
mainPresenter.loadData();
}
});
}
//======================下面是继承mvp/view包下的IMainView需要实现的方法=========================
@Override
public void showDialog() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideDialog() {
mProgressBar.setVisibility(View.GONE);
}
@Override
public void showData(String data) {
mTextView.setText(data);
Toast.makeText(this, "请求数据成功", Toast.LENGTH_SHORT).show();
}
}
我们看到上面的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、接下来是大家之前比较关心mvpView包下IMainView接口的代码:
package com.mvptest.mvp.view;
public interface IMainView {
void showDialog();//显示progressbar进度
void hideDialog();//隐藏progressbar进度
void showData(String data);//显示网络请求数据 }
IMainView就是一个接口,里面3个方法,作用是把网络请求数据结果回掉(返回)给MainActivity(UI)更新。怎么个回掉呢,不急后面会说,现在我们只要知道这个接口有什么方法。
小结:
到这里我们可以知道mvp中的v,也就是view层指什么了,也就是和直接操作UI有关的东西,我们这里具体指MainActivity、xml布局、IMainView,但是我并没有把MainActivity放到mvp/view包下,只是为了更加清晰的说明
4、mvp/presenter包下MainPresenter代码:
package com.mvptest.mvp.presenter;
import com.mvptest.mvp.model.ILoadDataListener;
import com.mvptest.mvp.model.MainModle;
import com.mvptest.mvp.view.IMainView;
public class MainPresenter {
private IMainView mIMainView;
private MainModle mMainModle;
public MainPresenter(IMainView mIMainView) {
this.mIMainView = mIMainView;
this.mMainModle = new MainModle();
}
/** * 加载网络数据 */
public void loadData() {
//回掉显示进度的progressbar接口
mIMainView.showDialog();
//请求数据
mMainModle.getData(new ILoadDataListener() {
@Override
public void loadSuccess(String data) {
//回掉隐藏进度的progressbar接口
mIMainView.hideDialog();
//回掉显示返回请求数据的接口
mIMainView.showData(data); } });
}
}
前面说完了mvp中的v,也就是view层,现在到mvp中的p,也就是presenter层了,而且前面我们也说了presenter的职责是什么,它负责view和model中间的协调,但是不互相影响。presenter也只是负责协调,其他逻辑或者显示UI都不会去做,只做自己的事情,那就是给view和model充当一座桥。
我们看到上面MainPresenter全是都是接口的调用(MainModle后面会说到),并没有做业务逻辑或UI处理等其他事情,因为我们一开头就说了mvp每一层都有自己 的职责,不会多做其他不属于自己的事情。
小结:
我们已经了解了mvp中view层,现在到presenter层,开头我们说了它作用就是连接view和modle的桥梁,在上面MainPresenter代码中也看到,只做链接IMainView和MainModle(各种接口回掉)的事情,这里presenter层具体指MainPresenter这个类。
5、接下来下面是mvp/modle包下MainModle代码:
我们前面说完了mvp的view层、presenter层,现在到modle层了。
前面我们也说了modle层的职责,它的职责是什么?当然是处理业务逻辑等操作咯。
package com.mvptest.mvp.model;
import android.os.Handler;
/**
* Created by tu2460 on 2017/4/8.
*/
public class MainModle {
private Handler mHandler = new Handler();
public void getData(final ILoadDataListener loadDataListener){
//在这里去获取网络数据,为了简化这里就使用handler模拟了
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//回掉请求成功的接口
loadDataListener.loadSuccess("网络获取的数据");
}
},3000);
}
}
MainModle里的loadDataListener.loadSuccess("网络获取的数据")是一个网络请求监听接口,案例是写在里面合理些的,但是这里也是为了看得清楚些,就把他单独写出来了,放在了mvp/modle下ILoadDataListener。
6、下面是mvp/model包下ILoadDataListener代码:
package com.mvptest.mvp.model;
/**
* Created by tu2460 on 2017/4/8.
*/
public interface ILoadDataListener {
void loadSuccess(String data);
}
ok,我们看到上面MainModle做了什么呢?做了网络请求数据的业务操作,然后把结果通过ILoadDataListener也就是网络请求监听的接口处理。
小结:
我们前面知道了mvp中的view层,然后知道了presenter层,现在也知道了modle层,modle层的作用就是做一些业务逻辑处理等,而这里的modle层指:
项目mvp/modle包下的MainModle和ILoadDataListener(这个接口也可以写到MainModle里,根据个人喜好,MVP只是个思想)。
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里,代码如下:
package com.mvptest.base;/**
* Created by tu2460 on 2017/4/9.
*/
public interface MVPIBaseView {
void showDialog();
void hideDialog();
}
2、mvp/presenter包下的MainPresenter构造方法里每个都要绑定IMainView这样的view,我们也抽取出来,另外还有一个事情要说,就是presenter会持有view,比如网络请求,这时如果返回退出了Activity,后台异步的动作不会立即停止,这里就会有内存泄漏的隐患,所以会在presenter中加入一个销毁view的方法。
MVPBasePresenter代码如下:
package com.mvptest.base;import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
/**
* Created by tu2460 on 2017/4/9.
*/
public class MVPBasePresenter<T> {
protected Reference<T> mIView;//View接口类型的弱引用
public void attachView(T iView){
mIView=new WeakReference<T>(iView);//建立关系
}
protected T getView(){
return mIView.get();
}
public boolean isViewAttached(){
return mIView!=null&&mIView.get()!=null;
}
public void detachView(){
if (mIView!=null){
mIView.clear();
mIView=null;
}
}
}
3、mvp的view层和presenter都抽取了公共部分,而modle层就这个demo来看,没有什么公用的部分就不做抽取了,但我们还得写一个MVPBaseActivity类,用于绑定和销毁mvp/view包下IMainView这样的,传递进去的上下文,因为这些步骤也是重复的。
MVPBaseActivity代码:
package com.mvptest.base;import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
/**
* Created by tu2460 on 2017/4/9.
*/
public abstract class MVPBaseActivity<V, T extends MVPBasePresenter<V>> extends AppCompatActivity {
protected T mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();//创建presenter
mPresenter.attachView((V) this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
protected abstract T createPresenter();
}
4、最后我们看看我们原来的MainActivity有什么变化,代码如下:
package com.mvptest;import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.mvptest.base.MVPBaseActivity;
import com.mvptest.mvp.presenter.MainPresenter;
import com.mvptest.mvp.view.IMainView;
public class MainActivity extends MVPBaseActivity<IMainView,MainPresenter> implements IMainView {
private TextView mTextView;
private Button mButton;
private ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
}
@Override
protected MainPresenter createPresenter() {
return new MainPresenter();
}
private void initView() {
mTextView = (TextView) findViewById(R.id.tv_data);
mButton = (Button) findViewById(R.id.bt_data);
mProgressBar = (ProgressBar) findViewById(R.id.pb);
}
private void initListener() {
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//网络请求加载数据
mPresenter.loadData();
}
});
}
//======================下面是继承mvp/view包下的IMainView需要实现的方法=========================
@Override
public void showDialog() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideDialog() {
mProgressBar.setVisibility(View.GONE);
}
@Override
public void showData(String data) {
mTextView.setText(data);
Toast.makeText(this, "请求数据成功", Toast.LENGTH_SHORT).show();
}
}
上面和原来比有什么变化呢? private MainPresenter mainPresenter = new MainPresenter(this);换成了在重写的createPresenter()方法里了,另外按钮点击事件里的对象使用的是MVPBaseActivity里的mPresenter了。
总结:
mvp只是开发多了的人而提出的一种开发模式,方法罢了,具体怎么用还是得看自己。下面该文章例子的MVP基本写法demo,需要的可以下载看看。当你熟练后,就可以对自己的项目以MVP思想进行属于相应项目和你的封装了。