转载请标明出处:
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请点击这里