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


更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. 【阿里云镜像】切换阿里巴巴开源镜像站镜像——Debian镜像
  4. Android屏幕分辨率正确获取及PX,DPI,DP,SP等的对应关系
  5. 《Android开发从零开始》——25.数据存储(4)
  6. Android系统配置数据库注释(settings.db)
  7. Android中不同应用间实现SharedPreferences数据共享
  8. android 获取唯一标识
  9. android拍照与读取相册

随机推荐

  1. MySQL常用函数
  2. MySQL中的运算符
  3. MySQL索引之哈希索引和自适应哈希索引(Ada
  4. 【转载】 MySQL数据库“十宗罪”(十大经典
  5. MySQL之mysqlcheck、check、optimize和an
  6. 【MySQL】有关登录连接的几个参数(max_con
  7. MySQL中日期转换 FROM_UNIXTIME和UNIX_TI
  8. 一段蛋疼的代码:超不清视频播放器
  9. 【EMCC】 12.1.0.5 OEM server agent 安
  10. MySQL InnoDB 共享表空间和独立表空间