优化你的代码结构 --- MVP
code小生,一个专注于 Android 领域的技术分享平台
作者:JYcoder
链接:https://www.jianshu.com/p/1f91cfd68d48
声明:本文是 JYcoder 原创,转发等请联系原作者授权。
安卓基础开发库,让开发简单点。
DevRing & Demo地址:https://github.com/LJYcoder/DevRing
学习/参考地址:
https://www.jianshu.com/p/91c2bb8e6369
http://www.jianshu.com/p/9d40b298eca9
http://blog.csdn.net/lmj623565791/article/details/46596109
MVP是什么?
Most Valuable Player(最有价值运动球员)? 不不不,虽然我很喜欢看nba,但此MVP非彼MVP。
这里的MVP是指安卓中的一种开发模式。
它将代码整体分为M(Model)、V(View)、P(Presenter)三层。
正经版:
M层(model):数据模型/处理层。负责数据处理、数据提供,如网络请求,数据库操作
V层(view):视图展示层。负责界面展示,如Activity,Fragment
P层(presenter):业务逻辑层。负责业务逻辑服务,是V层与M层间的桥梁
MVP示意图1
你也可以这样帮助理解下(餐厅版):
M层(model):厨师。负责做菜
V层(view):顾客。点餐吃饭
P层(presenter):服务员。提供下单、上菜等各种服务
MVP示意图2
与MVC模式的区别:
MVC中,V层与M层是可以互通的,而在MVP中V层与M层是不通的。
按餐厅版来说就是,MVC中顾客可以直接告诉厨师要吃什么菜,厨师做好后直接把菜端到你面前,而MVP中只能通过服务员来完成点餐到用餐的过程。
使用MVP有什么好处?
抽象些来说:
MVP可以降低代码耦合度,提高代码的结构清晰度、可读性、维护性和复用性。
具体些来说(参考JessYan的例子):
现在有这么一个需求:Activity中从网络获取数据然后展示在A控件上。
如果不用MVP的话,那就直接把获取展示等代码都写在Activity中,很快便可以写完。
但现在需求变动了:
1.要求加入缓存功能,如果本地有数据,则先从本地获取数据,然后再从网络获取最新数据进行替换
2.要求数据展示在B控件上而不是A控件。
如果代码都是你自己写的,那改起来还比较轻松,但假如是团队开发,代码不是你写的,你需要花时间把逻辑重新看一遍再开始改,而且如果改错的话,会影响之前已经写好的功能。
但使用MVP模式进行开发就不同了。由于它的分工结构清晰,V层仅负责数据展示,P层仅负责业务逻辑,M层仅负责数据获取/处理。所以改动起来就轻松很多。
对于变动的需求1:我们只需在P层加入逻辑判断(先从本地获取,再网络获取),然后M层增加一个从本地获取数据方法。
对于变动的需求2:我们只需在V层修改获取到数据后的展示方式,从控件A改成控件B。
当然还有可复用等优点,这里就不具体讲了。
至于"缺点"嘛,就是会相应地增加代码量。有得有失,但得大于失。
具体使用
以这么一个场景为例:
从网络获取“正在上映”的电影数据,获取成功则将数据在页面展示,获取失败则给出相应提示。
需要写四个部分:Model层,View层,Presenter层,接口
接口
负责“连接”MVP三层,以便方法调用、数据流动。同时也便于进行单元测试。
IView
View层接口,定义View层需实现的方法,P层通过该接口回调通知View层。
public interface IMovieView { //成功获取到电影数据 void getMovieSuccess(List list, int type) ; //获取电影数据失败 void getMovieFail(int status, String desc, int type);}
IModel
Model层接口,定义Model层需实现的方法,P层通过该接口调用M层获取/处理数据的方法。
public interface IMovieMoel{ //请求正在上映的电影数据 Observable getPlayingMovie(int start, int count); ...}
Model层
实现IModel接口中的方法,负责数据的获取/处理。
public class MovieModel implements IMovieMoel{ @Override public Observable getPlayingMovie(int start,int count) { //提供数据源 return DevRing.httpManager().getService(MovieApiService.class).getPlayingMovie(start, count); } ...}
Presenter层
处理业务逻辑,调用M层获取数据,调用V层传递展示数据。
public class MoviePresenter { private IMovieView mIView; private IMovieModel mIModel; public MoviePresenter(IMovieView iMovieView, IMovieMoel iMovieMoel) { mIView = iMovieView; mIModel = iMovieModel; } /** * 获取正在上映的电影 * * @param start 请求电影的起始位置 * @param count 获取的电影数量 * @param type 类型:初始化数据INIT、刷新数据REFRESH、加载更多数据LOADMORE */ public void getPlayingMovie(int start, int count, final int type) { DevRing.httpManager().commonRequest( mIModel.getPlayingMovie(start, count), new CommonObserver>>() { @Override public void onResult(HttpResult> result)
{ if (mIView != null) { mIView.getMovieSuccess(result.getSubjects(), type); } } @Override public void onError(int errType, String errMessage) { if (mIView != null) { mIView.getMovieFail(errType, errMessage, type); } } }, RxLifecycleUtil.bindUntilDestroy(mIView)); } ... /** * 释放引用,防止内存泄露 */ public void destroy() { mIView = null; }}
View层
实现IView接口中的方法,对获取到的数据进行展示
public class MovieFragment implements IMovieView { //获取电影数据成功的网络请求回调 @Override public void getMovieSuccess(List list, int type) { //成功,对数据进行展示 .... } //获取电影数据失败的网络请求回调 @Override public void getMovieFail(int status, String desc, int type) { //失败,界面上做出相应提示 ... }}
完成以上几步后,在View层初始化时,调用Presenter层方法即可。
@Overrideprotected void initData() { mPresenter = new MoviePresenter(this, new MovieModel()); mPresenter.getPlayingMovie(start, mCount, type);}
还有一点需注意:
如果Presenter层持有了View层的引用,那么记得在V层销毁时,把Presenter层中对View层的引用置null,避免View层回收失败导致内存泄漏。
@Overridepublic void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.destroy(); mPresenter = null; } }
MVP相关
从google todo-mvp示例再次学习MVP
一步一步带你认识 MVP+Retrofit+Rxjava 并封装(二)
2018.04.20
更多相关文章
- Android之动画(Java代码实现)1
- Android中使用XmlSerializer拼装XML数据
- 【第一行代码】Android日志工具
- android:sqlite 数据库的事务详解
- Android 本地代码中的LIKELY和UNLIKELY宏
- Android移动应用基础学习——第四章数据存储
- Cocos2dx android 部分代码的理解
- 【Android】内嵌html页面与native代码简单交互
- android学习笔记之十一数据存储(Shared Preferences、SQLite)