我们一起去学习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中的类,如下:
2.实体类建好了,我们需要去服务器或者网络取我们这个例子所需要的数据,然而android中的MVP架构模式中加载数据是在model层,所以我们在model层写了一个QueryPhoneModeld的接口,接口具体代码很简单,就是定义了一个去取数据的方法,如下:
这个方法里面传了2个参数,一个是OnQueryPhoneListener,一个是String类型的参数.现在我就用我自己的理解先解释一下这两个参数的作用。
1>.OnQueryPhoneListener 是一个定义的接口,因为我们去服务器或者网络上取数据的过程,我们需要知道,我去取数据的时候是成功了还是失败了呢·?所以我们需要定义一个接口去监听它去取数据的过程,如下是OnQueryPhoneListenter接口中的代码:
2>.String 类型的参数,是从view层回调过来的参数,用来构建完整的URL去请求我们所需要的数据。
3.去取数据的方法定义好了·毕竟是抽象出来的·接下来我们就去实现它·让它具体的去服务器取数据,或者存储数据等一些操作。然后我们就写了一个QueryPhoneModelImpl类去实现(implements)QueryPhoneModel接口。QueryPhoneModelImpl类具体代码如下:
4.好了·现在在我们的例子的Model层取数据的过程已经完成了,取好的数据我们需要显示在View层,而MVP模式中View层跟Model层是不能直接联系的,所以现在我们就需要一个连接View层与Model的纽带-Presenter.在Presenter层我们需要去Model层拿数据然后回调给View层,这样我们就完美的完成了一系列的操作了,首先我们在Presenter定义一个接口去 Model层拿数据即:QueryPhonePresenter,接口里面定义的方法很简单,如下图所示:
既然我们在Presenter定义了接口,跟前面的Model层一样·我们新建一个QueryPhonePresenter接口的实现类即:QueryPhonePresenterImpl:
里面的代码如下:
在这里面·我们首先利用了构造方法把这个Presenter所关联的View传进来,即:QueryPhoneView,QueryPhoneView是一个接口,里面定义了一些界面上显示所需要的一些方法·
这样子·我们的Presenter层就与它所关联的 View层有联系了··而Model层是怎么样跟Presenter层联系的呢·?看这段代码:
我们在构造方法中直接给New出来一个Model层的实例·这样在Presenter层有了Model层的实例,接下来我们就很好办了·我们定义Presenter层·无非就是为了让Model层跟View层是不直接联系的··他们之间是通过Presenter来进行联系的·看下面代码·
在这个方法里面·我们可以看到·我们调用了Model层的getData()方法去取数据,在这个方法里·我们传了2个参数,一个是OnQueryPhoneListener,监听它获取数据是失败了还是成功了·一个是·String 类型的参数·因为我们所需要获取数据的URL需要从View层·获取一个用户指定的数据来构建的·这里我们直接调用queryPhoneView中的getPhoneNub()方法就行·
在这里·我们是去取数据·我们取数据的状态是成功还是失败·这里我假设成功··那么成功之后·代码就走PresenterImpl中的OnSuccess方法·
从这个方法可以看出来·我们成功获取到数据之后·我们直接调用了QueryPhoneView中的setPhoneInfo()的方法··直接把数据传给了View层·到这里·我们一系列的取数据操作结束··从代码中可以看出来·model层·与view层是没有直接联系的·而是通过中间纽带presenter把他们给关联起来了·
5.好吧··终于要介绍最后一个东西了·View层··在MVP模式中的View层··就是咋们的Activity或者Fragment或者一些跟用户交互的一些view.
从结构图中我们看到有一个QueryPhoneActivity,现在我们来看看这个activity中的代码,
我们把这个activity直接实现了QueryPhoneView这个接口·然后直接new出来了一个PresenterImpl的实例··如下代码:
然后通过用户点击查询按钮·进行一系列的操作·我们在按钮的点击事件中的代码是·直接调用了·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欢迎围观·
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(实践-让我们开始写代码-Android框架学习-2.service)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-5.Android中的进程与线程)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-8.AndroidManifest.xml文件)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-5.设置(Settings))
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-1.用户界面和布局)
- 我们的Android学习轨迹
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-6.对话框)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-3. 菜单)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-6.对话框)
- 据说程序员是最爱学习的群体,IT男都知道,这个行业日新月异,必须不断地学习新知识,不断地为自己注入新鲜的血液,才能使自己跟上技术的步伐。 今天我们来讲一下Android中BroadcastReceiv
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-6.权限(Permissions))
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-3. 菜单)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-2. 输入控件)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-2. 输入控件)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-7.通知)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-4.Intents和Intent Filters)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-1.用户界面和布局)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-5.设置(Settings))
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-4.Action Bar)
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-7.通知)