MVP基本了解及使用和封装

前言

想了解一个新事物我会按以下步骤来:

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、接下来是大家之前比较关心mvp/view包下IMainView的代码:
 
 
 
package com.mvptest.mvp.view; /** * Created by tu2460 on 2017/4/8. */ 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代码:
 
 
 
上面说完了mvp中的v,也就是view层,现在到mvp中的p,也就是presenter层了,而且前面我们也说了presenter的职责是什么,它负责view和model中
 
间的协调,但是不互相影响。presenter也只是负责协调,其他逻辑或者显示UI都不会去做,只做自己的事情,给view和model充当一座桥。
 
 
 
package com.mvptest.mvp.presenter; import com.mvptest.mvp.model.ILoadDataListener; import com.mvptest.mvp.model.MainModle; import com.mvptest.mvp.view.IMainView; /** * Created by tu2460 on 2017/4/8. */ 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); } }); } }
 

我们看到上面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是一种思想架构,一种开发模式,上面的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进行相应的封装。

demo下载

猜你喜欢

转载自blog.csdn.net/u014769864/article/details/69668740