浅谈Android中的MVP

转载请标明出处:

http://blog.csdn.net/hai_qing_xu_kong/article/details/51745798

本文出自:【顾林海的博客】

前言

为什么使用MVP,网上有很多说法,最主要就是减轻了Activity的责任,相比于MVC中的Activity承担的责任太多,因此有必要讲讲MVP。

MVP入门

在MVC框架中,View是可以直接读取Model模型中的数据的,Model模型数据发生改变是会通知View数据显示发生相应的改变。而在MVP中Model和View之间的没有任何联系,是两个完全独立的模块,当Model模型发生数据改变时,通过Presenter通知View视图发生相应的UI改变。因此,个人觉得:MVP才是正真的视图和模型完全分离,也就是Model模型进行业务数据处理和View视图显示没有任何关联。可以通过下图看出:

以下是MVC的框架图:

以下是MVP的框架图:

有关MVP说明的文章现在网上一大堆,最近自己也在尝试使用MVP去构建应用,关于MVP三层的定义,可以看看下面相关资料:

model

数据加工处理厂

model是整个应用或界面的数据加工处理厂,所谓数据加工厂就是对数据的获取,数据的解析,数据的存储,数据的分发,数据的增删改查等操作。意思就是凡是涉及到数据操作都是在model进行的,所以model不仅仅只是实体类的集合,同时还包含关于数据的各种处理操作。

三种数据源

数据的数据源有三种:内存,磁盘(文件或数据库等),网络。为了提升app的性能,有必要把经常访问的数据临时存入内存中;同时也为了提升app性能和为用户省流量省电,有必要把数据存入磁盘中;还有的数据是有必要从网络读取的。三个数据源不一定同时存在,比如不与网络交互的app,不存在网络数据源。所以凡是涉及到关于数据发生于三个数据源加工处理的操作的代码都要放在model中。

model为上层提供的服务:

model从黑盒的角度来看为上层(指依赖于model的层比如present)提供的服务无非就2种:model为上层提供数据,model处理上层传递的数据

model为上层提供数据:

上层会从model中去数据,那model会从三数据源中取数据,取的顺序是

  • 先内存,内存取到数据返回
  • 其次磁盘,磁盘取到数据,如有必要把数据存储在内存中,则需要进行存储,返回数据
  • 最后网络,网络取到数据,如有必要在磁盘或内存中存储,则进行存储,返回数据

上面的取数据过程是最简单的情况,复杂些还会涉及到从内存或磁盘中取到的数据是否过期,过期的话就应该从网络获取。从网络取得数据后需要把内存或磁盘的数据更新。

model处理上层传递的数据:

model接收到上层传递的数据后,model会依次把数据扔给三个数据源去处理,有可能三个数据源都会处理数据,有可能只是其中一个处理,model会把处理的结果返回。

所以model会把解析好的数据提供给上层,上层对于数据的来源完全是透明的,上层完全不需要关心数据到底是来自内存,还是磁盘甚至是网络。同理上层只需要的把数据扔给model,上层唯一做的事情就是愉快的等待处理结果。

presenter

presenter翻译成汉语的意思是主持人,提出者。从它的意思可以看出它有控制全场的作用。首先presenter是处于mvp的中间层,在view和model中起一个承上启下的作用。presenter会把view交给自己的命令进行一定的校验等操作交给model处理,会把model处理的结果交给view。

presenter封装业务:

presenter不仅起一个桥梁的作用,它还会把业务逻辑代码给包揽下来。这样就可以减轻Activity的负担了,让Activity全心全意做它的view工作。那估计就有朋友犯迷糊了,哪些代码属于业务逻辑呢?比如一些校验代码。或者可以这样想只要是不属于view和model的代码基本都可以放在presenter中。

presenter负责刷新view:

mvc或以前的关于view的写法一般都是这样,view在接收到数据后,自己来进行view的刷新或其他操作。但是mvp中presenter负责对view进行刷新,比如从model获取的数据,presenter会根据获取的数据成功与否来通知view应该是显示成功界面还是失败界面。这样就让Activity变的更轻了,变成了听别人指挥的傻白甜了。这时候的presenter就有点主持人,掌控者的味道了。

presenter持有的线程:

Android中view的操作需要在ui线程里执行,其他耗时操作需要在普通线程执行。presenter会持有这2种线程:ui线程,普通线程。刷新view时,它切换为ui线程进行刷新,从model取数据切换为普通线程。假如使用rxjava的话,就特别简单了关于线程切换的事情。

view

view层就很好理解了,就是用户直接看到的界面,mvp中的view是很省心的,比如更新view,接收数据。这些操作它都不需要操心,也不需要知道数据到底来自哪里,给我啥我显示啥就可以了。

一个view可以同时拥有多个presenter,也可以只有一个presenter。

Android中的Activity,Fragment在mvp中是作为view来使用的,这些Activity,Fragment的责任就小了,只关心界面相关的事情足矣。各种Adapter是放在view层的。

案例展示

既然清楚了MVP的一些概念,现在就可以创建一个项目,整个项目很简单,通过点击按钮获取天气信息并显示在界面上,这里面我们使用MVP来搭建整个项目,先从Mode开始到Presenter最后View的创建。(天气接口使用的是心知天气提供的SDK http://www.thinkpage.cn/doc#info )

案例Model层

代码展示:

package weather.weatherproject.mode;

import com.thinkpage.lib.api.TPCity;
import com.thinkpage.lib.api.TPListeners;
import com.thinkpage.lib.api.TPWeatherManager;
import com.thinkpage.lib.api.TPWeatherNow;

import weather.weatherproject.WeatherApplication;

/**
 * 天气管理类
 * Created by glh on 2016-06-23.
 */
public class WeatherManager {

    public static final WeatherManager instance = new WeatherManager();

    public interface WeatherListener{
        void onSuccess(TPWeatherNow response);

        void onFailed(String errString);
    }

    /**
     * 获取指定城市的实况天气。
     *
     * @param city
     * @param listener
     */
    public void getNowWeather(String city, final WeatherListener listener) {

        WeatherApplication.weatherManager.getWeatherNow(new TPCity(city), TPWeatherManager.TPWeatherReportLanguage.kSimplifiedChinese, TPWeatherManager.TPTemperatureUnit.kCelsius, new TPListeners.TPWeatherNowListener() {
            @Override
            public void onTPWeatherNowAvailable(TPWeatherNow tpWeatherNow, String s) {
                if (tpWeatherNow != null) {
                    listener.onSuccess(tpWeatherNow);
                } else {
                    listener.onFailed(s);
                }
            }
        });
    }

}

我们知道Model层主要用于数据的输入和输出,因此这里创建了一个天气管理类,通过获取天气信息的方法获取数据,最后将数据传递给Presenter,Presenter只需要将城市名传递给Model层,而它只需监听获取信息的状态,因此内部创建了一个天气信息获取的监听接口,通过它使得Presenter进行信息获取的监听。

案例Presenter层

代码展示:

package weather.weatherproject.presenter;

/**
 * View的基础接口
 * Created by glh on 2016-06-23.
 */
public interface IView {
    void initView();
}
package weather.weatherproject.presenter;

/**
 *
 * Created by glh on 2016-06-23.
 */
public interface IPresenter<V extends IView> {
    void onStop();

    void onResume();

    void onDestroy();

    void onPause();

    void onStart();

    void init(V view);
}

IView是一个基础接口,内部定义了一个initView方法,用于View的初始化。

IPresenter也是一个基础接口,内部定义了一些 Activity或Fragment常用的生命周期方法,用于View与 Activity或Fragment的联动。

package weather.weatherproject.presenter.weather;

import weather.weatherproject.presenter.IPresenter;
import weather.weatherproject.presenter.IView;
import weather.weatherproject.view.bean.NowWeather;

/**
 * 获取天气的约定类,用于组合IWeatherView和IWeatherPresenter
 * Created by glh on 2016-06-23.
 */
public interface WeatherContract {
    interface IWeatherView extends IView {
        //获取指定城市的实况天气
        void showNowWeather(NowWeather result);

        void error(String error);
    }

    interface IWeatherPresenter extends IPresenter<IWeatherView> {
        void getWeather(String city);
    }
}

可以看到WeatherContract接口内部定义两个接口:

1、IWeatherView接口,用于Presenter与View的数据传递。

2、IWeatherPresenter接口,是连接Model与View的中间层。

package weather.weatherproject.presenter.weather;

import com.thinkpage.lib.api.TPWeatherNow;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import weather.weatherproject.mode.WeatherManager;
import weather.weatherproject.presenter.bean.BeanUtil;

/**
 * 获取天气的Presenter
 * Created by glh on 2016-06-23.
 */
public class WeatherPresenter implements WeatherContract.IWeatherPresenter {

    private WeatherContract.IWeatherView mIWeatherView;
    private WeatherManager mWeatherManager = WeatherManager.instance;
    private ExecutorService mExecutorService = Executors.newFixedThreadPool(5);

    @Override
    public void init(WeatherContract.IWeatherView view) {
        this.mIWeatherView = view;
        mIWeatherView.initView();
    }

    @Override
    public void getWeather(final String city) {
        mExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                mWeatherManager.getNowWeather(city, new WeatherManager.WeatherListener() {
                    @Override
                    public void onSuccess(TPWeatherNow response) {
                        mIWeatherView.showNowWeather(BeanUtil.createNowWeather(response));
                    }

                    @Override
                    public void onFailed(String errString) {
                        mIWeatherView.error(errString);
                    }
                });
            }
        });
    }

    @Override
    public void onStop() {

    }

    @Override
    public void onResume() {

    }

    @Override
    public void onDestroy() {

    }

    @Override
    public void onPause() {

    }

    @Override
    public void onStart() {

    }

}

WeatherPresenter主要做了以下几件事情:

1、初始化了View。

2、通过View传递过来的指令向Model层请求数据。

3、监听Model层的状态,并将结果刷新到View上。

请求数据属于耗时操作因此我们开辟了线程用与请求处理,最后通过UI线程刷新View。BeanUtil用于封装我们所需的数据。

案例View层

代码展示:

package weather.weatherproject.view.base;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;

import java.util.HashSet;
import java.util.Set;

import weather.weatherproject.presenter.IPresenter;

/**
 * 基类Activity,所有业务界面都继承此BaseActivity。
 * Created by glh on 2016-06-23.
 */
public abstract class BaseActivity extends FragmentActivity {
    private Set<IPresenter> mAllPresenters = new HashSet<>(1);

    /**
     * 获取layout的id,具体由子类实现
     *
     * @return
     */
    protected abstract int getLayoutResId();

    /**
     * 需要子类来实现,获取子类的IPresenter,一个activity有可能有多个IPresenter
     */
    protected abstract IPresenter[] getPresenters();

    /**
     * 初始化presenters
     */
    protected abstract void onInitPresenters();

    /**
     * 事件监听
     */
    protected abstract void initEvent();

    /**
     * 从intent中解析数据,具体子类来实现
     *
     * @param argIntent
     */
    protected void parseArgumentsFromIntent(Intent argIntent) {
    }

    private void addPresenters() {

        IPresenter[] presenters = getPresenters();
        if (presenters != null) {
            for (int i = 0; i < presenters.length; i++) {
                mAllPresenters.add(presenters[i]);
            }
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutResId());
        if (getIntent() != null) {
            parseArgumentsFromIntent(getIntent());
        }
        addPresenters();
        onInitPresenters();
        initEvent();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //依次调用IPresenter的onResume方法
        for (IPresenter presenter : mAllPresenters) {
            if (presenter != null) {
                presenter.onResume();
            }
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        //依次调用IPresenter的onStop方法
        for (IPresenter presenter : mAllPresenters) {
            if (presenter != null) {
                presenter.onStop();
            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        //依次调用IPresenter的onPause方法
        for (IPresenter presenter : mAllPresenters) {
            if (presenter != null) {
                presenter.onPause();
            }
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        //依次调用IPresenter的onStart方法
        for (IPresenter presenter : mAllPresenters) {
            if (presenter != null) {
                presenter.onStart();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //依次调用IPresenter的onDestroy方法
        for (IPresenter presenter : mAllPresenters) {
            if (presenter != null) {
                presenter.onDestroy();
            }
        }
    }
}

BaseActivity只是做了一下封装,方便我们展示界面的使用,最后我们看看展示界面WeatherActivity:

package weather.weatherproject.view.weather;

import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import weather.weatherproject.R;
import weather.weatherproject.presenter.IPresenter;
import weather.weatherproject.presenter.weather.WeatherContract;
import weather.weatherproject.presenter.weather.WeatherPresenter;
import weather.weatherproject.view.base.BaseActivity;
import weather.weatherproject.view.bean.NowWeather;

/**
 * 天气界面
 * Created by glh on 2016-06-23.
 */
public class WeatherActivity extends BaseActivity implements WeatherContract.IWeatherView {

    private WeatherPresenter mWeatherPresenter = new WeatherPresenter();

    private TextView tv_show;
    private Button btn_now_weather;

    @Override
    protected int getLayoutResId() {
        return R.layout.activity_main;
    }

    @Override
    protected IPresenter[] getPresenters() {
        return new IPresenter[]{mWeatherPresenter};
    }

    @Override
    protected void onInitPresenters() {
        mWeatherPresenter.init(this);
    }

    @Override
    protected void initEvent() {
        btn_now_weather.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(WeatherActivity.this, "onclick", Toast.LENGTH_SHORT).show();
                mWeatherPresenter.getWeather("shanghai");
            }
        });
    }

    @Override
    public void showNowWeather(NowWeather result) {
        tv_show.setText(result.toString());
    }

    @Override
    public void error(String error) {
        tv_show.setText(error);
    }

    @Override
    public void initView() {
        tv_show = (TextView) findViewById(R.id.tv_show);
        btn_now_weather = (Button) findViewById(R.id.btn_now_weather);
    }
}

最后可以看到我们的Activity非常的简洁,到了这里我们的项目已经搭建完成,这样做有以下好处:

1、学习过设计模式的人都知道,这样做基本符合了单一职责原则。

2、符合单一职责原则后,导致类与类组织更清晰。

3、View层与Model层交互需要通过Presenter层进行,这样v与m层级间的耦合性降低。

4、通过这种分层处理,每一层的测试也相对简单,维护性更高。

项目地址

MVPDemo请点击这里

时间: 2024-10-09 17:23:37

浅谈Android中的MVP的相关文章

浅谈Android中的MVC与MVP模式

使用MVC或者MVP模式会增加很多的类,但是确可以让代码结构变得清晰,方便了后期维护拓展方便.把数据层跟视图层分离,处理事务的逻辑单独的放在一个类中,让Activity仅仅具有展示功能. 下面我们就MVC模式跟MVP模式进行分别讲解,总之来说各有利弊.在实际的开发中,我们根据实际情况进行取舍.个人认为MVP模式更简单一些,因为MVP模式中会把部分逻辑Activity中,但是这就造成了Activity的相对繁琐,没有实现完全的隔离.而我们采用的MVC模式则是更好的处理了这个问题,但是在应用的过程中

浅谈android中手机联系人字母索引表的实现

实际上字母索引表的效果,可以说在现在的众多APP中使用的非常流行,比如支付宝,微信中的联系人,还有购物,买票的APP中选择全国城市,切换城市的时候,这时候的城市也就是按照一个字母索引的顺序来显示,看起来是很方便的.其实这种字母索引表的效果最开始是出现在微信的联系人中.因为觉得这种效果功能在今后的项目中可以说是非常常见,可能会用的上,所以准备来波博客讲述一下实现的原理,一来方便以后自己复习,二来如果能够帮助一些android路上奋斗小伙伴也是蛮有意义的. 下面我们先来看下效果图, 看完效果图后我们

浅谈安卓中的MVP模式

端午放假,天气下雨,于是乎在家撸一下博客,本篇博客将为大家解析MVP模式在安卓中的应用. 本文将从以下几个方面对MVP模式进行讲解: 1.  MVP简介 2.  为什么使用MVP模式 3.  MVP模式实例 4.  MVP中的内存泄露问题 1.  MVP简介: 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MVP(Model

浅谈android中图片处理之图形变换特效Matrix(四)

今天,我们就来谈下android中图片的变形的特效,在上讲博客中我们谈到android中图片中的色彩特效来实现的.改变它的颜色主要通过ColorMatrix类来实现. 现在今天所讲的图片变形的特效主要就是通过Matrix类来实现,我们通过上篇博客知道,改变色彩特效,主要是通过ColorMatrxi矩阵的系数,以及每个像素点上所对应的颜色偏移量.而今天的图形变换与那个也是非常的类似.它是一个3*3矩阵,而颜色矩阵则是一个4*5的矩阵.在这个3*3矩阵中则表述出了每个像素点的XY坐标信息.然后通过修

浅谈android中仅仅使用一个TextView实现高仿京东,淘宝各种倒计时

今天给大家带来的是仅仅使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.最近公司一直加班也没来得及时间去整理,今天难得休息想把这个分享给大家,只求共同学习,以及自己后续的复习.为什么会想到使用一个TextView来实现呢?因为最近公司在做一些优化的工作,其中就有一个倒计时样式,原来开发的这个控件的同事使用了多个TextView拼接在一起的,实现的代码冗余比较大,故此项目经理就说:小宏这个就交给你来优化了,并且还要保证有一定的扩展性,当时就懵逼了.不知道从何处开始

浅谈android中的异步加载一

1.为什么需要异步加载. 因为我们都知道在Android中的是单线程模型,不允许其他的子线程来更新UI,只允许UI线程(主线程更新UI),否则会多个线程都去更新UI会造成UI的一个混乱有些耗时的操纵(例如网络请求等),如果直接放到主线程中去请求的话则会造成主线程阻塞,而我们系统有规定的响应时间,当响应的时间超过了了阻塞的时间就会造成"Application No Response",也就是我们熟知的ANR错误解决上述问题的时候:我们一般使用的是线程或者线程池+Handler机制如果线程

浅谈android中只使用一个TextView实现高仿京东,淘宝各种倒计时

今天给大家带来的是只使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.近期公司一直加班也没来得及时间去整理,今天难得歇息想把这个分享给大家.只求共同学习,以及自己兴许的复习. 为什么会想到使用一个TextView来实现呢?由于近期公司在做一些优化的工作,当中就有一个倒计时样式,原来开发的这个控件的同事使用了多个TextView拼接在一起的.实现的代码冗余比較大.故此项目经理就说:小宏这个就交给你来优化了.而且还要保证有一定的扩展性,当时就懵逼了.不知道从何处開始

浅谈android中的ListView合集系列之解决ScrollView和ListView嵌套冲突(一)

相信大家都已经可以熟练使用ListView和GridView,大神们估计都在使用RecyclerView了.如果还在使用ListView,你肯定有这样的一个深刻的感受,那就是在做一个APP的时候使用ListView和GridView很频繁,并且经常会遇到一个页面中除了有ListView或GridView可能还有一些其他的内容,但是可能内容很多,你第一时间就会想到让它整体滑动即可,那就是在总的布局外面包裹一个ScrollView.也就是出现了ScrollView中嵌套一个ListView的场景,或

浅谈android中图片处理之色彩特效处理ColorMatrix(三)

在android开发中对图片处理很是频繁,其中对图片的颜色处理就是很常见的一种.我们经常看到一些类似美图秀秀,美颜相机的app,为什么那么黑的人拍出来是确实那么地白呢?长的那么那个(丑)的人,用美颜相机拍出来的看起来也有那么回事(拍出来就感觉挺漂亮).就像网上有个段子,有钱的都去韩国了,没钱都用ps了.韩国的就去整形,中国的就用ps.这些话虽然是调侃,但是从某种程度上来说像类似美图秀秀,美颜相机app确实挺受大家欢迎.但是你是否曾想过它这种效果,它是怎么实现的吗?你是否曾想过它的原理是什么吗?所