您的位置:首页 > 移动开发 > Android开发

我们一起去学习android中的MVP

2016-09-13 18:22 441 查看
MVP架构介绍:

MVP是Model、View、Presenter的缩写,分别代表项目中3个不同的模块。



Model:它主要是负责数据的加载或者存储,比如从服务器或者从本地数据库获取数据等。

View:主要是负责数据的展示或者是与用户的交互等。

Presenter:主要是Model与View两者之间建立关系并且完成交互的桥梁,MVP的核心之一就是Presenter,它能使Model与View两者之间分离开,达到解耦的目的。

MVP优点

降低耦合度,隐藏数据,Activity中代码更简洁

模块职责划分明显

代码复用度较高

代码灵活性

演示demo:

前面我们学习了MVP的基本概念的介绍,现在我们来动手写一个例子。

这是一个查询电话号码归属地的的例子,接口我是在聚合数据平台申请的,然后网络请求数据的框架我是用的是Volley。

项目的基本结构,如下图:



好的结构图出来了,我现在就按着我的理解我的思路来编写这个例子。

1.首先我们要建立一个实体类也就是结构图中的QueryPhoneInfo中的类,如下:

public class QueryPhoneInfo {
private String resultcode;
private String reason;
private ResultBean result;
private int error_code;

public String getResultcode() {
return resultcode;
}

public void setResultcode(String resultcode) {
this.resultcode = resultcode;
}

public String getReason() {
return reason;
}

public void setReason(String reason) {
this.reason = reason;
}

public ResultBean getResult() {
return result;
}

public void setResult(ResultBean result) {
this.result = result;
}

public int getError_code() {
return error_code;
}

public void setError_code(int error_code) {
this.error_code = error_code;
}

public static class ResultBean {
private String province;
private String city;
private String areacode;
private String zip;
private String company;
private String card;

public String getProvince() {
return province;
}

public void setProvince(String province) {
this.province = province;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getAreacode() {
return areacode;
}

public void setAreacode(String areacode) {
this.areacode = areacode;
}

public String getZip() {
return zip;
}

public void setZip(String zip) {
this.zip = zip;
}

public String getCompany() {
return company;
}

public void setCompany(String company) {
this.company = company;
}

public String getCard() {
return card;
}

public void setCard(String card) {
this.card = card;
}
}
}


2.实体类建好了,我们需要去服务器或者网络取我们这个例子所需要的数据,然而android中的MVP架构模式中加载数据是在model层,所以我们在model层写了一个QueryPhoneModeld的接口,接口具体代码很简单,就是定义了一个去取数据的方法,如下:

public interface QueryPhoneModel {
//定义一个方法去获取数据
void getData(OnQueryPhoneListener listener,String nub);
}


这个方法里面传了2个参数,一个是OnQueryPhoneListener,一个是String类型的参数.现在我就用我自己的理解先解释一下这两个参数的作用。

1>.OnQueryPhoneListener 是一个定义的接口,因为我们去服务器或者网络上取数据的过程,我们需要知道,我去取数据的时候是成功了还是失败了呢·?所以我们需要定义一个接口去监听它去取数据的过程,如下是OnQueryPhoneListenter接口中的代码:

public interface OnQueryPhoneListener {
//定义一些获取数据时的状态的方法
void OnSuccess(QueryPhoneInfo queryPhoneInfo);
void OnFailed();
}


2>.String 类型的参数,是从view层回调过来的参数,用来构建完整的URL去请求我们所需要的数据。

3.去取数据的方法定义好了·毕竟是抽象出来的·接下来我们就去实现它·让它具体的去服务器取数据,或者存储数据等一些操作。然后我们就写了一个QueryPhoneModelImpl类去实现(implements)QueryPhoneModel接口。QueryPhoneModelImpl类具体代码如下:

public class QueryPhoneModelImpl implements QueryPhoneModel {
//具体是怎么去取数据的实现
private String URL = "http://apis.juhe.cn/mobile/get?";

@Override
public void getData(final OnQueryPhoneListener listener, String nub) {
Map<String, String> params = new HashMap<>();
params.put("phone", nub);
params.put("dtype", "");
params.put("key", "46066e52ff348681e2e3b22669c0cd89");
VolleyManager.newInstance().GsonPostRequest("a", params, URL, QueryPhoneInfo.class, new Response.Listener<QueryPhoneInfo>() {
@Override
public void onResponse(QueryPhoneInfo response) {
if (response.getResult() == null) {
listener.OnFailed();
} else {
listener.OnSuccess(response);
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
listener.OnFailed();
}
});
}
}


4.好了·现在在我们的例子的Model层取数据的过程已经完成了,取好的数据我们需要显示在View层,而MVP模式中View层跟Model层是不能直接联系的,所以现在我们就需要一个连接View层与Model的纽带-Presenter.在Presenter层我们需要去Model层拿数据然后回调给View层,这样我们就完美的完成了一系列的操作了,首先我们在Presenter定义一个接口去 Model层拿数据即:QueryPhonePresenter,接口里面定义的方法很简单,如下图所示:

public interface QueryPhonePresenter {
//定义一个方法去model层去取数据
void getData();
//这个方法再后面解释,我们现在只看getData()方法,去Model层取数据
void onDestroy();
}


既然我们在Presenter定义了接口,跟前面的Model层一样·我们新建一个QueryPhonePresenter接口的实现类即:QueryPhonePresenterImpl:

里面的代码如下:

public class QueryPhonePresenterImpl implements QueryPhonePresenter, OnQueryPhoneListener {
private QueryPhoneView mqueryPhoneView;
private QueryPhoneModel queryPhoneModel;

public QueryPhonePresenterImpl(QueryPhoneView queryPhoneView) {
this.mqueryPhoneView = queryPhoneView;
queryPhoneModel = new QueryPhoneModelImpl();
}

@Override
public void getData() {
mqueryPhoneView.showLoading();
queryPhoneModel.getData(this,mqueryPhoneView.getPhoneNub());
}

@Override
public void onDestroy() {
this.mqueryPhoneView=null;
}

@Override
public void OnSuccess(QueryPhoneInfo queryPhoneInfo) {
mqueryPhoneView.setPhoneInfo(queryPhoneInfo);
mqueryPhoneView.hideLoading();
}

@Override
public void OnFailed() {
mqueryPhoneView.hideLoading();
mqueryPhoneView.showError();
}
}


在这里面·我们首先利用了构造方法把这个Presenter所关联的View传进来,即:QueryPhoneView,QueryPhoneView是一个接口,里面定义了一些界面上显示所需要的一些方法·

public interface QueryPhoneView {
void setPhoneInfo(QueryPhoneInfo info);

void showLoading();

void hideLoading();

void showError();

String getPhoneNub();
}


这样子·我们的Presenter层就与它所关联的 View层有联系了··而Model层是怎么样跟Presenter层联系的呢·?看这段代码:

public QueryPhonePresenterImpl(QueryPhoneView queryPhoneView) {
this.mqueryPhoneView = queryPhoneView;
queryPhoneModel = new QueryPhoneModelImpl();
}


我们在构造方法中直接给New出来一个Model层的实例·这样在Presenter层有了Model层的实例,接下来我们就很好办了·我们定义Presenter层·无非就是为了让Model层跟View层是不直接联系的··他们之间是通过Presenter来进行联系的·看下面代码·

public void getData() {
mqueryPhoneView.showLoading();
queryPhoneModel.getData(this,mqueryPhoneView.getPhoneNub());
}


在这个方法里面·我们可以看到·我们调用了Model层的getData()方法去取数据,在这个方法里·我们传了2个参数,一个是OnQueryPhoneListener,监听它获取数据是失败了还是成功了·一个是·String 类型的参数·因为我们所需要获取数据的URL需要从View层·获取一个用户指定的数据来构建的·这里我们直接调用queryPhoneView中的getPhoneNub()方法就行·

在这里·我们是去取数据·我们取数据的状态是成功还是失败·这里我假设成功··那么成功之后·代码就走PresenterImpl中的OnSuccess方法·

public void OnSuccess(QueryPhoneInfo queryPhoneInfo) {
mqueryPhoneView.setPhoneInfo(queryPhoneInfo);
mqueryPhoneView.hideLoading();
}


从这个方法可以看出来·我们成功获取到数据之后·我们直接调用了QueryPhoneView中的setPhoneInfo()的方法··直接把数据传给了View层·到这里·我们一系列的取数据操作结束··从代码中可以看出来·model层·与view层是没有直接联系的·而是通过中间纽带presenter把他们给关联起来了·

5.好吧··终于要介绍最后一个东西了·View层··在MVP模式中的View层··就是咋们的Activity或者Fragment或者一些跟用户交互的一些view.

从结构图中我们看到有一个QueryPhoneActivity,现在我们来看看这个activity中的代码,

public class QueryPhoneActivity extends Activity implements QueryPhoneView {
@BindView(R.id.et_queryphone)
EditText etQueryphone;
@BindView(R.id.tv_province)
TextView tvProvince;
@BindView(R.id.tv_area)
TextView tvArea;
@BindView(R.id.tv_company)
TextView tvCompany;
@BindView(R.id.tv_card)
TextView tvCard;

public QueryPhonePresenter queryPhonePresenter;
@BindView(R.id.btn_query)
Button btnQuery;

private String nub;
private ProgressDialog dialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_queryphone);
ButterKnife.bind(this);
initview();
}

private void initview() {
queryPhonePresenter = new QueryPhonePresenterImpl(this);
dialog = new ProgressDialog(QueryPhoneActivity.this);
dialog.setMessage("正在加载中...");
}

@Override
public void setPhoneInfo(QueryPhoneInfo info) {
if (info != null && info.getError_code() == 0) {
tvProvince.setText(info.getResult().getProvince());
tvArea.setText(info.getResult().getCity());
tvCard.setText(info.getResult().getCard());
tvCompany.setText(info.getResult().getCompany());
}
}

@Override
public void showLoading() {
dialog.show();
}

@Override
public void hideLoading() {
if (dialog.isShowing()) {
dialog.dismiss();
}
}

@Override
public void showError() {
Toast.makeText(QueryPhoneActivity.this, "出错了", Toast.LENGTH_SHORT).show();
}

@Override
public String getPhoneNub() {
return nub = etQueryphone.getText().toString().trim();
}

@Override
protected void onDestroy() {
super.onDestroy();
queryPhonePresenter.onDestroy();
}

@OnClick({R.id.btn_query})
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_query:
queryPhonePresenter.getData();
break;

}
}


我们把这个activity直接实现了QueryPhoneView这个接口·然后直接new出来了一个PresenterImpl的实例··如下代码:

queryPhonePresenter = new QueryPhonePresenterImpl(this);


然后通过用户点击查询按钮·进行一系列的操作·我们在按钮的点击事件中的代码是·直接调用了·Presenter层的getData()方法··然后处理一些数据的显示··可以看出··我们现在的activity里的代码很清晰··一点都不乱··看起来也舒服多了··也达到我们解耦的目的··

好的· ·我们来一起总结一下这个例子···首先·用户通过界面的查询按钮·进行交互·在用户输入信息·之后点击了按钮·这时候在activity(view层)响应了用户的点击·然后在onClick()方法中·我们通过Presenter层的实例调用了getData()方法·而在Presenter层在getData()方法中我们又通过Model层的实例调用了Model层的getData(OnQueryPhoneListener listener,String nub)方法,这时候到了Model层了·在Model层的getData(OnQueryPhoneListener listener,String nub)方法中我们具体操作了这么去服务器取数据·然后取回来的数据通过OnQueryPhoneListener回调到Presenter层,而Presenter层的实现类PresenterImpl实现了OnQueryPhoneListener`回调回来的数据会走·OnSuccess(QueryPhoneInfo queryPhoneInfo)这个方法·然后我们在这个方法中通过View层的setPhoneInfo(queryPhoneInfo)方法把数据传到了view层然后我们在view层通过回传过来的数据进行显示操作就就行了···这一系列的操作就此结束了···

通过这个例子·我们看到·我们使用mvp模式相对于mvc模式来讲··增加了很多代码与很多类·具体怎么用·就看个人实际项目中的取舍了·

关于presenter一直持有Activity对象导致的内存泄漏问题,Presenter会持有view,如果Presenter有后台异步的长时间的动作,比如网络请求,这时如果返回退出了Activity,后台异步的动作不会立即停止,这里就会有内存泄漏的隐患,所以会在presenter中加入一个销毁view的方法。

具体操作是这样子的·在Presenter的接口中·我们定义了一个Ondestory()方法·然后在Presenter的实现类PresenterImpl中的·Ondestory()方法中把this.mqueryPhoneView=null;这样子··我们的操作就会随着activity执行onDestory()方法而停止·

哈哈·终于结束啦··前几个月写的学习计划·我应该是在加强基础··不过我现在发现有一些很热门的技术我需要先学习·先用上·比如mvp·rxjava ·Retrofit,fresco等等·我都需要去学习··到现在为止我基本上学完了·然后我会一一的总结出来做个记录···然后写一个小项目把所有学过的技术用上去··再然后我就去复习我的基础去了···下一篇博客·我们一起去学习·Retrofit的联网框架。谢谢·终于写完了···拜拜·

https://github.com/MrXiaoChao/MVP 这个是本例子的demo欢迎围观·
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android mvp
相关文章推荐