优化你的代码结构 --- MVP

安卓基础开发库,让开发简单点。
Demo地址https://github.com/LJYcoder/MvpDagger

学习/参考地址:
https://www.jianshu.com/p/91c2bb8e6369
http://www.jianshu.com/p/9d40b298eca9
http://blog.csdn.net/lmj623565791/article/details/46596109

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<MovieRes> 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<HttpResult<List<MovieRes>>>() {
            @Override
            public void onResult(HttpResult<List<MovieRes>> 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 MovieActivity extends Activity implements IMovieView {
    //获取电影数据成功的网络请求回调
    @Override
    public void getMovieSuccess(List<MovieRes> list, int type) {
        //成功,对数据进行展示
        ....
    }

    //获取电影数据失败的网络请求回调
    @Override
    public void getMovieFail(int status, String desc, int type) {
        //失败,界面上做出相应提示
        ...
    }

    ...
}

完成以上几步后,在View层初始化时,调用Presenter层方法即可。

@Override
protected void onCreate(Bundle saveInstanceState) {
      ...
      mPresenter = new MoviePresenter(this, new MovieModel());
      mPresenter.getPlayingMovie(start, mCount, type);
}

还有一点需注意:
如果Presenter层持有了View层的引用,那么记得在V层销毁时,把Presenter层中对View层的引用置null,避免View层回收失败导致内存泄漏。

@Override
public void onDestroy() {
     super.onDestroy();
     if (mPresenter != null) {
          mPresenter.destroy();
          mPresenter = null;
     }
 }

2018.4.13:
github的MvpDagger项目中新增了MVP一键生成模板,根据Demo的代码结构定制的,有需要的可以查看。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 前言 看了下上篇博客的发表时间到这篇博客,竟然过了11个月,罪过,罪过。这一年时间也是够折腾的,年初离职跳槽到鹅厂...
    西木柚子阅读 21,110评论 12 185
  • 一直很纠结,这个Parse.Cloud到底是什么鬼?后来,经过简单代码阅读与实际编写实践,发现,挺有意思! 简单的...
    NextStack阅读 739评论 0 0
  • 上学期间勤工俭学时,我去过韩城。那里的农村,有些村庄的人家住的很分散,马路边上靠近小山坡的向阳位置是最受欢迎的,三...
    桂丑阅读 203评论 7 13
  • 我们经常看到有人一直很努力,却是一事无成,是什么原因影响的呢?姑且不论方法和方向有没有问题,却总能看到Ta同时想兼...
    Louise718阅读 155评论 2 5