MVP是Model-View-Presenter的简称,即模型-视图-表现层的缩写。MVP是由MVP模式进化而来的,MVP改进了MVC中的控制器过于臃肿的问题。 与MVC一样,MVP将应用程序的数据处理、数据显示和逻辑控制分开,用一种业务逻辑、数据显示和界面相分离的方法组织代码。
MVP模式是MVC模式在Android上的一种变体,要介绍MVP就得先介绍MVC。在MVC模式中,Activity应该是属于View这一层。而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。
MVC | MVP | |||
---|---|---|---|---|
Model | 业务逻辑和实体模型 | Model | 业务逻辑和实体模型 | |
View | 对性欲布局文件 | View | 对应于Activity,负责view的绘制和用户交互 | |
Controller | 对应于Activity等 | Presenter | 负责完成view与model的交互,处理程序逻辑 |
MVP模式是MVC模式在Android上的一种变体,要介绍MVP就得先介绍MVC。在MVC模式中,Activity应该是属于View这一层。而实质上,它既承担了View,同时也包含一些Controller的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把Activity的View和Controller抽离出来就变成了View和Presenter,这就是MVP模式。
MVP与MVC相比,MVP减少了Activity的职责,简化了Activity的代码,将复杂的逻辑代码提取到了Presenter中进行处理。Presenter的出现,将Activity视为View层,Presenter负责完成View层与Model层的交互。与之对应的好处就是:程序耦合度更低,更加方便地进行测试,程序可扩展性大大提高。
MVP从MVC演化而来,它们的基本思想有相通的地方。Controller与Presenter负责逻辑的处理,Model提供数据,View负责显示数据。MVP作为一个新的模式,与MVC有一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部;而在MVC中View会直接从Model读取数据。
MVP解决了MVC问题: 在MVP中,Presenter完全把View与Model进行分离,主要的程序逻辑在Presenter实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View层的时候可以保持Presenter不变。不仅如此,我们还可以编写测试用的View,模拟用户的操作,从而实现对Presenter的测试——而不需要使用自动化的测试工具。 MVP中的View层是很薄的一层,View只应该有简单的set/get方法、用户输入和界面显示的内容,除此之外不应该有更多的内容,绝不允许直接访问Model——这就是MVP与MVC的很大不同之处。
由于对View的操作放在了Presenter中,所以View和Presenter的交互会过于频繁。如果Presenter过多地操作视图,往往会使得它与特定的 View联系过于紧密。一旦视图需要改变,那么Presenter也需要改变。
首先看一下项目的基本结构图:
$ tree
.
├── model
│ ├── IPestModel.java
│ └── PestModelImpl.java
├── presenter
│ ├── BasePresenter.java
│ └── PestPresenter.java
└── view
├── BaseActivity.java
└── IPestView.java
项目结构看起来像是这个样子的,MVP的分层还是很清晰的。我的习惯是先按模块分Package,在模块下面再去创建model、view、presenter的子Package,当然也可以用model、view、presenter作为顶级的Package,然后把所有的模块的model、view、presenter类都到这三个顶级Package中,就好像有人喜欢把项目里所有的Activity、Fragment、Adapter都放在一起一样。
BasePresenter.java
package com.ahau.againstpest.presenter;
import java.lang.ref.WeakReference;
/**
* 注释:
*
* @author fanchunchun
* @date 2018/4/3
*/
public class BasePresenter<T > {
//1, view 层的引用
protected WeakReference<T> mViewRef;
//进行绑定
public void attachView(T view) {
mViewRef = new WeakReference<T>(view);
}
//进行解绑
public void detachView() {
mViewRef.clear();
}
}
PestPresenter.java
package com.ahau.againstpest.presenter;
import com.ahau.againstpest.model.IPestModel;
import com.ahau.againstpest.model.PestModelImpl;
import com.ahau.againstpest.view.IPestView;
/**
* 注释:表示层
*
* @author fanchunchun
* @date 2018/4/3
*/
public class PestPresenter<T extends IPestView> extends BasePresenter<T> {
//2, model层的引用
IPestModel pestModel = new PestModelImpl();
//3, 构造方法
public PestPresenter() {
}
//4, 执行操作(UI)逻辑
public void fetch() {
if (mViewRef.get() != null) {
if (pestModel != null) {
//获取pest的名字
pestModel.getPest(new IPestModel.PestGetListener() {
@Override
public void onComplete(String[] pest) {
if (mViewRef.get() != null) {
mViewRef.get().showPest(pest);
}
}
});
//设置当前时间
pestModel.getDate(new IPestModel.DateListener() {
@Override
public void onComplete(String date) {
if (mViewRef.get() != null) {
mViewRef.get().setDate(date);
}
}
});
pestModel.getLatlng(new IPestModel.LatlngListener() {
@Override
public void onComplete(String[] latlng) {
if (mViewRef.get() != null) {
mViewRef.get().setLatlng(latlng);
}
}
});
}
}
}
}
BaseActivity.java
package com.ahau.againstpest.view;
import android.app.Activity;
import android.os.Bundle;
import com.ahau.againstpest.presenter.BasePresenter;
/**
* 注释:
*
* @author fanchunchun
* @date 2018/4/3
*/
public abstract class BaseActivity<V,T extends BasePresenter<V>> extends Activity{
//表示层的引用
public T pestPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pestPresenter = createPresenter();
pestPresenter.attachView((V) this);
}
protected abstract T createPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
pestPresenter.detachView();
}
}
IPestView.java
package com.ahau.againstpest.view;
/**
* 注释:定义出所有UI逻辑
*
* @author fanchunchun
* @date 2018/4/3
*/
public interface IPestView {
//显示DropEditText的数据(使用会掉的方式返回数据)
void showPest(String[] pest);
//显示当前时间
void setDate(String date);
//获取当前的经纬度
void setLatlng(String[] latlng);
}
IPestModel.java
package com.ahau.againstpest.model;
/**
* 注释:用来加载数据
*
* @author fanchunchun
* @date 2018/4/3
*/
public interface IPestModel {
void getPest(PestGetListener pestGetListener);
//设计一个内部回调接口
interface PestGetListener {
void onComplete(String[] pest);
}
void getDate(DateListener dateListener);
interface DateListener {
void onComplete(String date);
}
//获取当前经纬度数据
void getLatlng(LatlngListener latlngListener);
interface LatlngListener {
void onComplete(String[] latlng);
}
}
package com.ahau.againstpest.model;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import com.ahau.againstpest.HttpProcessor.HttpCallback;
import com.ahau.againstpest.HttpProcessor.HttpHelper;
import com.ahau.againstpest.base.MyApplication;
import com.ahau.againstpest.bean.PestBean;
import com.ahau.againstpest.utils.URLUtils;
import com.amap.api.maps2d.CoordinateConverter;
import com.amap.api.maps2d.model.LatLng;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 注释:
*
* @author fanchunchun
* @date 2018/4/3
*/
public class PestModelImpl implements IPestModel {
/**害虫的名字
* @param pestGetListener
*/
@Override
public void getPest(final PestGetListener pestGetListener) {
Map<String, Object> params = new HashMap<>();
HttpHelper.obtain().get(URLUtils.PestUrl, params, new HttpCallback<PestBean>() {
@Override
public void onSuccess(PestBean pestBean) {
ArrayList<PestBean.Pest> mPestNameList;
String[] arr; //pest种类
mPestNameList = pestBean.pest;
Log.i("TAG", pestBean.toString());
/**
* 设置虫种
*/
arr = new String[mPestNameList.size() + 1];
arr[0] = "请选择:";
for (int i = 0; i < mPestNameList.size(); i++) {
arr[i + 1] = mPestNameList.get(i).pestname;
}
Log.i("tag", arr[0]);
pestGetListener.onComplete(arr);
}
@Override
public void onFailure(String e) {
Log.e("tag", "网络请求失败");
}
});
}
/**
* 当前日期
* @param dateListener
*/
@Override
public void getDate(DateListener dateListener) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(" yyyy/MM/dd ");// yyyy/MM/dd HH:mm:ss
//获取当前时间
Date date = new Date(System.currentTimeMillis());
String sDate = simpleDateFormat.format(date);
dateListener.onComplete(sDate);
}
/**
* 经纬度数据
* @param latlngListener
*/
@Override
public void getLatlng(LatlngListener latlngListener) {
String[] latlng = new String[2];
String jingdu, weidu, gaodejingdu, gaodeweidu; //经纬度
LocationManager locationmanager = (LocationManager) MyApplication.getInstance().getSystemService(Context.LOCATION_SERVICE);
if (ActivityCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MyApplication.getInstance(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Location location = locationmanager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
double lat = location.getLatitude();// 经度
double lng = location.getLongitude();// 纬度
jingdu = "" + lat;
weidu = "" + lng;
} else {
location = locationmanager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
double lat = location.getLatitude();// 经度
double lng = location.getLongitude();// 纬度
jingdu = "" + lat;
weidu = "" + lng;
} else {
jingdu = "";
weidu = "";
}
}
if (!jingdu.isEmpty() && !weidu.isEmpty()) {
LatLng latLng = new LatLng(Double.valueOf(jingdu), Double.valueOf(weidu));
CoordinateConverter converter = new CoordinateConverter();
// CoordType.GPS 待转换坐标类型
converter.from(CoordinateConverter.CoordType.GPS);
// sourceLatLng待转换坐标点 LatLng类型
converter.coord(latLng);
// 执行转换操作
LatLng desLatLng = converter.convert();
gaodejingdu = "" + desLatLng.latitude;
gaodeweidu = "" + desLatLng.longitude;
}
latlng[0] = jingdu;
latlng[1] = weidu;
latlngListener.onComplete(latlng);
}
}