Android MVP Contract分析

2023-05-16

相关文章:

Android MVP - Contract

谷歌官方MVP Contract分析

View: 只处理UI及页面效果的细节,向Presenter暴露更新UI的方法;并且持有Presenter的引用,通过Presenter对其暴露的方法进行一些初始化页面以及业务提交等动作,但不关注动作的具体实现。

Presenter: 只关注业务逻辑的细节,持有View的引用,通过调用View层向其暴露的方法去更新UI (这里的View引用不是具体某个控件的引用,我们也不能让Presenter持有某一控件的引用);并且也持有一个或者多个model的引用(在于你想将Presenter,也就是业务逻辑拆分的程度,避免Presenter也像MVC中Controller一样被撑爆),可以使用model,通过对数据库或者网络的访问从而拿到数据,调用View暴露的方法去刷新UI。

Model:向Presenter暴露获取、存储、提交数据等方法,具体实现细节Presenter不关注;Model通过Callback 将数据返回给Presenter。

Ok,按照之前规定的原则:

  • View 向 Presenter 暴露更新UI的方法,于是我们有了 IView
  • Presenter 向 View 暴露执行一些特定业务方法,比如初始化页面,提交等。

于是,就产生了第一种MVP模式:

 

看到以上类图,可能会有人有疑问:

  1. 在IView中为什么我不直接写 updateView(Response r),这样不是写的接口方法更少吗? 这个问题问的好!因为我曾经也有过这样的问题,试想一下,如果我们把Model中的Response直接存在于Activity(View层)中,那当我们更改Response的时候,会导致View层也需要进行相应的变动,还能做到View和Model的完全解耦吗?所以View 向 Presenter暴露的方法参数一定要符合两点: (1). 基本数据类型 (2). 公共数据类型
  2. 为什么要写IPresenter,让Activity直接引用 XXXPresenter不就好了? 确实,这样做从功能上来说也可以,但是,为了更严格一些,让Presenter向View暴露那些View关注的方法,这样开发View的同学就会一下子明白自己只需要关注哪些方法就够了;同理,Presenter持有IView的引用而不是Activity的也是这个原因。

按照以上模式,我们用伪代码来实现一下第一种MVP模式(以下代码不严谨,只表达意思):

Presenter:

interface IPresenter {
        void initData();

        void commit();
    }

    class XXXPresenter implements IPresenter {
        private IView mView;
        private Model mModel;
        private Callback mCallback = new Callback() {
            public void onSuccess(Response r) {
                mView.cancelLoading();
                if (r.url == 'initDataUrl') {
                    mView.updateHeader(r.header);
                    mView.updateContent(r.content);
                } else if (r.url == 'commitUrl') {      // 页面跳转或者其他什么的
                }
            }

            public void onError(Error e) {
                mView.showError();
            }
        };

        XXXPresenter(IView view) {
            mView = view;
            mModel = new Model();
        }

        @Override
        public void initData() {
            mView.showLoading();
            mModel.request(xx, xx, mCallback);
        }

        @Override
        public void commit() {
            mView.showLoading();
            mModel.request(xx, xx, mCallback);
        }
    }

View:

interface IView {
        void updateHeader(String header);

        void updateContent(String content);

        void showLoading();

        void cancelLoading();

        void showError(String error);
    }

    public class XXXActivity extends BaseActivity implements IView {
        private IPresenter mPresenter;
        private TextView tvHeader;
        private TextView tvContent;

        public void onCreate(Bundle saveInstanceState) {
            super.onCreate(saveInstanceState);
            setContentView(layout);
            initView();
            initData();
        }

        public void initView() {
            tvHeader = findViewById(..);
            tvContent = findViewById(..);
            tvHeader.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    mPresenter.commit();
                }
            });
        }

        public void initData() {
            mPresenter = new XXXPresenter(this);
            mPresenter.initData();
        }

        @Override
        public void updateHeader(String header) {
            tvHeader.setText(header);
        }

        @Override
        public void updateContent(String content) {
            tvContent.setText(content);
        }
    }

由于 showLoading、cancelLoading 以及 showError都是公用方法,所以你可以把它们放到 BaseActivity中,不用每个Activity都实现一遍。

讲完了吗?Contract在哪呢? 好汉不要急,待我娓娓道来~
以上的MVP确实解决了两个问题:

  1. Activity过重
  2. View与Model解耦

但是,我觉得还没有解决好,哪里还有问题呢:

  1. IView 每次都要声明 showLoading等公共方法
  2. Presenter中每次请求时都要调用mView的showLoading等公共方法
  3. 能不能把IView和IPresenter定义在同一个文件里,这样我就能一眼看出这个页面的业务是什么样子的。

带着这几个问题,Google官方的MVP Contract横空出世:

https://github.com/googlesamples/android-architecture

关注一下工程结构,如下:

如果我们按照模块划分包的话,最适合这种模式,我们来看下一个模块下的组成:
AddEditTaskContract

  • 由于我们再BasePresenter中因为网络请求可能会调用到View的showLoading等方法,所以BasePresenter我写成了抽象类,并且指定一个<? extends BaseView>的泛型;
  • 上图指定一个Presenter的泛型,目的是为了:
public interface BaseView<T> {
        void setPresenter(T presenter);
    }

AddEditTaskFragment

AddEditTaskPresenter

而Presenter的实例化完全可以放到Activity中执行,所以并不需要给BaseView指定Presenter的泛型(可能有点绕。。。。。。),在按照之前第一种MVP模式的业务逻辑再上一张优化后的类图 :

MVP 之 Contract

话不多说,直接贴一段优化后的伪代码:

//BaseView:
    public interface BaseView {
        public void showLoading();
        public void cancelLoading();
        public void showError();
    } 

    //BaseActivity
    public class BaseActivity extends Activity implements BaseView {    
        public void showLoading() {
                LoadingUtil.showLoading(this);
            }    
        public void cancelLoading() {
                 LoadingUtil.cancelLoading(this);
            }    
        public void showError() {
                 LoadingUtil.showError(this);
            }
    }

    //BasePresenter:
    public abstract class BasePresenter<View extends BaseView> {    
        private View mView;    
        protected Callback mCallback = new Callback() {             
            public void onStart() {
                          mView.showLoading();
               } 
            public void onSuccess(Response r) {
                          mView.cancelLoading();
                          onRequestSuccess(r);
                }             
            public void onError(Error e) {
                         mView.showError();
                         onRequestFailed(e);
                }
               } ;       
            protected abstract void onRequestSuccess(Response r);       
            protected abstract void onRequestFailed(Error e);
    }
    // Contractinterace 
        Contract {    
            interface View extends BaseView {        
                void updateHeader(String header);        
                void updateContent(String content);
            }   
            abstract class Presenter extends BasePresenter<View> {       
                protected Presenter(View view) {           
                    super(view);
                    }       
                protected abstract void initData();       
                protected abstract void commit();
                    
                }
    
        }
// 具体的

XXPresenterclass XXPresenter extends Contract.Presenter {     
            private Model mXXModel;     
            protected XXPresenter(View view) {           
                super(view);
                mXXModel = new Model();
            }    
            protected void initData() {
                 mXXModel.request(xx, xx, mCallback);
            }    
            protected void commit() {
            mXXModel.commit(xx, xx, mCallback);
            }    
            protected void onRequestSuccess(Response r) {          
                if (r.url == 'initDataUrl') {
                   mView.updateHeader(r.header);
                   mView.updateContent(r.content);
                } else if (r.url == 'commitUrl') {              
                // 页面跳转或者其他什么的
                }
             }    
            protected void onRequestFailed(Error e) {         
            // 做一些其他的error逻辑,基类已经调用baseview的showerror
            }
    }
 // XXActivity
    public class XXActivity extends BaseActivity implements Contract.View {       
        private Contract.Presenter mPresenter;       
        private TextView tvHeader;       
        private TextView tvContent;   
    
        public void onCreate(Bundle saveInstanceState) {          
              super.onCreate(saveInstanceState);
              setContentView(layout);
              initView();
              initData();
        } 

        public void initView() {
             tvHeader = findViewById(..);
             tvContent = findViewById(..);
             tvHeader.setOnClickListener(new OnClickListener(){             
             public void onClick(View v) {
                 mPresenter.commit();
             }
             });
        }     
        public void initData() {
         mPresenter = new XXPresenter(this);
         mPresenter.initData();
         }     
         @Override
         public void updateHeader(String header) {
             tvHeader.setText(header);
         }    
         @Override
         public void updateContent(String content) {
             tvContent.setText(content);
         }    
    }

如果是一个View组件而不是Activity或者Fragment,也可以使用,那就需要我们把BaseActivity对于View的公共处理抽成一个BaseViewHelper,这样尽量多地复用代码,代码我就不贴了哈,这也是个题外话~

​​​​

结束语

以上就是MVP的Contract写法,单看Contract,个人感觉还是比较清晰的,而且再加一部分的代码复用,我相信会让你的app更加敏捷地迭代~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android MVP Contract分析 的相关文章

随机推荐

  • 多进程生产者消费者框架设计

    前言 介绍了进程如何基于面向对象的封装 xff0c 本章我们基于封装好的Process类来实现一种无锁版的生产者和消费者框架 xff0c 用它实现了高性能文件拷贝功能 读这篇文章之前大家可以想一下如果是你 xff0c 你会怎么设计这样的框架
  • golang实现简单rpc调用

    RPC通信过程 RPC的通信过程网上介绍很多 xff0c 这里就不在单独介绍了 xff0c 具体过程如下 xff1a 1 Client以本地调用的方式发起调用 xff1b 2 Client stub收到调用后负责将被调用的方法名 参数等打包
  • 一文彻底搞懂leveldb架构

    leveldb leveldb是一个写性能十分优秀的存储引擎 xff0c 是典型的LSM tree的实现 LSM的核心思想是为了换取最大的写性能而放弃掉部分读性能 那么 xff0c 为什么leveldb写性能高 xff1f 简单来说它就是尽
  • flink核心之watermarker

    背景介绍 xff1a 现在的社会 xff0c 人们产生越来越多的数据 xff0c 而数据对每个人人都产生了巨大的影响 比如你去银行贷款 xff0c 那么必然银行要对你做信用评估 xff0c 会涉及到大数据画像等数据分析 比如美团外卖 xff
  • 我在华为度过的 “两辈子”(学习那些在大厂表现优秀的人)

    七 是一个很神奇的数字 生物学中认为组成身体的细胞 xff0c 七年会彻底更新一遍 xff1b 李笑来说 xff0c 七年就是一辈子 xff0c 每一辈子至少要习得一个重要的技能 xff0c 进而获得不可逆的重生 xff1b 白居易写道 x
  • 【Effective Java】大厂实战之考虑以静态工厂方法代替构造方法

    图片 Item1 考虑以静态工厂方法代替构造方法 Item1 考虑以静态工厂方法代替构造方法 缺点1 只提供静态工厂方法的话 xff0c 该类就无法被继承 xff08 子类化 xff09 缺点2 API的查找比较麻烦 常见静态工厂方法的命名
  • 一文搞懂leveldb写操作

    前言 leveldb一直也它优秀的写性能而文明 xff0c 本篇文章我们就来分析下leveldb的写流程 来搞懂为什么它的写性能如此优秀 整理流程 leveldb一次写入分为两部分 xff1a 第一步先将写操作写入日志 第二步将写操作应用到
  • 来聊聊对象文件网关和分布式文件存储的区别

    前言 大家都知道 xff0c 存储系统一般分为块存储 对象存储和文件存储三种 其中文件存储的使用最广泛 xff0c 个人电脑 NAS 大到传统的HPC 大数据平台等等 这些都是以使用文件接口为主 最近几年 xff0c 由于成本低 存储空间大
  • Ubuntu中文件属性以及所属用户问题

    1 xff0c 查看文件属性 xff0c 命令如下 xff1a 查看当前目录下某个文件属性 ls l lt file name gt 查看当前目录下文件属性 ls l 查看所有当前文件下属性 ls al 在开始的 rwxr xr x 为该文
  • Android8.0 屏幕旋转180度

    一般手机只能旋转3个方向 xff0c 这里将介绍如何让手机可以旋转180度 xff0c 也就是上下颠倒 1 静态方法 frameworks base core res res values config xml config allowAl
  • 深度理解go中的Map

    这里写自定义目录标题 前言map的内存模型增量扩容查找过程分析插入过程分析 前言 本篇将从底层讲解map的赋值 删除 查询 扩容的具体执行过程 结合源码 xff0c 让你彻底明白map的原理 map的内存模型 在源码中 xff0c 表示ma
  • 在职场我们该具备哪些能力

    专才or 通才 不知道大家有没有这样的感觉 xff0c 现在的工作专业化程度越来越高 xff0c 细分粒度也越来越小 IT领域分到你是计算里面的数据库或者了流式计算引擎 xff0c 或者是协议存储还是KV存储引擎 专业化的程度带来了一个好处
  • 【zabbix Java开发教程】docker部署zabbix及api获取实战教程

    文章目录 Docker安装移除旧版本的docker环境安装必要系统工具添加源信息更新yum缓存安装Docker ce 启动Docker测试运行hello world 修改源 Docker部署zabbix创建zabbix的MySQL运行zab
  • go版本分布式锁redsync使用教程

    redsync使用教程 前言redsync结构Pool结构Mutex结构acquire加锁操作release解锁操作 redsync包的使用 前言 在编程语言中锁可以理解为一个变量 xff0c 该变量在同一时刻只能有一个线程拥有 xff0c
  • Fragment实例精讲——底部导航栏的实现(3)

    转自 xff1a http www runoob com w3cnote android tutorial fragment demo3 html 本节引言 前面我们已经跟大家讲解了实现底部导航栏的两种方案 xff0c 但是这两种方案只适合
  • Android NavigationBar 背景设置成完全透明不起作用

    lt item name 61 34 android navigationBarColor 34 gt 64 android color transparent lt item gt 使用 android navigationBarColo
  • 计算机视觉CV中RANSAC算法的学习笔记~

    1 致谢 感谢网友叶晚zd的博客 xff0c 原文链接如下 xff1a https blog csdn net u013925378 article details 82907502 2 RANSAC算法介绍 随机抽样一致算法 xff08
  • 【Java】JsonArray用法

    1 解析字符串为JsonArray JSONArray jsonArray 61 JSONArray parseArray str 2 存值 jsonArray add object 3 取值 for int i 61 0 i lt jso
  • 怎么用JMeter写性能测试脚本

    JMeter 可能是应用最广泛的性能测试工具 怎么用 JMeter 编写性能测试脚本 xff1f 一 脚本制作原则 我们一般写代码 xff0c 都会有代码规范 xff0c 比如写java有java规范 xff0c 写python有pytho
  • Android MVP Contract分析

    相关文章 xff1a Android MVP Contract 谷歌官方MVP Contract分析 View 只处理UI及页面效果的细节 xff0c 向Presenter暴露更新UI的方法 xff1b 并且持有Presenter的引用 x