美文网首页android应用框架android框架学习Android Dev
T-MVP:泛型深度解耦下的MVP大瘦身

T-MVP:泛型深度解耦下的MVP大瘦身

作者: North_2016 | 来源:发表于2016-05-06 14:48 被阅读22116次

    QQ群:AndroidMVP 555343041

    更新日志:

    2017/1/31:AOP新增SysPermissionAspect支持动态申请系统权限切片,轻松适配6.0+

    2017/1/27:AOP新增DbRealmAspect支持Realm数据库,数据库突破你想像的简单(年夜特供)

    2017/1/8: 使用Apt封装Retrofit生成ApiFactory替换掉所有的Repository,狂删代码

    2017/1/7: 使用DataBinding替换掉所有的ButterKnife,狂删代码

    2017/1/6: 使用DataBinding替换掉所有的ViewHolder,狂删代码,从此迈向新时代

    2016/12/30:新增TRouter全局路由,使用apt生成代码实现页面跳转

    2016/12/29:去掉BaseMultiVH新增VHClassSelector支持更完美的多ViewHolder

    2016/12/28:使用Apt生成全局的ApiFactory替代所有的Model

    2016/12/27:增加了BaseMultiVH扩展支持多类型的ViewHolder

    2016/12/26:抽离CoreAdapterPresenter优化TRecyclerView

    安卓AOP实战:面向切片编程

    Android实用技巧之:用好泛型,少写代码

    安卓AOP实战:APT打造极简路由

    全局路由TRouter,更优雅的页面跳转

    安卓AOP实战:Javassist强撸EventBus

    加入OkBus,实现注解传递事件

    安卓AOP三剑客:APT,AspectJ,Javassist

    1、去掉所有反射>2、新增apt初始化工厂,替换掉了dagger2。>3、新增aop切片,处理缓存和日志

    关键词 MVP+Retrofit+Rxjava+MaterialDesign+LeanCloud+NodeJS
    简书:http://www.jianshu.com/p/b49958e1889d
    weibo:http://weibo.com/1917320262
    Github:https://github.com/north2016/T-MVP
    QQ群:AndroidMVP 555343041

    导语

    删繁就简三秋树,领异标新二月花

    传统MVP给人的第一感觉通常是接口和类的暴涨,将Activity中除V之外的繁杂操作搬到P之后依然臃肿不堪。MVP从来都不是救命稻草,只能锦上添花,不能雪中送炭。也许在权衡利弊之后,很多人会对MVP望而却步。 现在 我们换一个思路,取其精华,去其糟粕,完成T-MVP大变身。

    一、T-MVP简介


    话不多说,先上图:


    T-MVP架构图

    下面,看看如何利用泛型把我们从繁杂重复的劳动中解救出来。

    后台接口用的是LeanCloud的REST API,数据通过Node爬虫从网页上获取,先爬出十个页面的列表,然后每一个列表爬进去获取文章详情。爬虫代码见app.js。(建议替换成自己申请的appId和appKey)。

    1、页面分层结构

    宗旨:纯粹界面操作交互,不需要MP参与的行为,尽量V自己做,保证MVP职责清晰,P只有干净简洁的协助VM的业务逻辑操作,M只处理数据操作。

    登录契约类

    相比谷歌官方Contract类,多了一个Model,用于将P中繁杂的数据操作分离解耦,让P层变得简洁明了,只处理M和V相关操作即可,登录Present的全部代码:

    登录Present的全部代码

    RxManage用于管理订阅者、观察者、以及事件。(详见代码)
    例如:
    发送事件: mRxManage.post(C.EVENT_LOGIN, user);
    接收事件:mRxManage.on(C.EVENT_LOGIN, arg ->mView.initUserInfo((_User) arg));

    LoginModel的全部代码:


    LoginModel的全部代码

    抽离出Model不仅各层更加分工明确便于Model的复用,而且大大简化了Presenter的代码量,让P层更简洁更专注。职能清爽,一目了然。

    2、列表页

    例如一个列表页,用户关心的,只有列表的Item展示:

    mTRecyclerView.setItemView(ItemView.class);

    不需要在当前页面写网络请求,不需要写下拉刷新和分页加载更多的回调,不需要写任何Adapter,只需要这一句即可。因为用泛型在TRecyclerView里面写过抽象层的操作,将监听和数据的获取封装成通用模板,从此可以一劳永逸了。

    比如,分页加载的封装操作:


    TRecyclerView部分代码

    对于有HeaderView的List也一样:

    mTRecyclerView.setHeaderView(HeaderView.class).setItemView(ItemView.class);

    ViewHolder也是相当简洁的写法:


    ViewHolder

    至此,项目可以永远告别OnRefresh ,onLoadMore ,Adapter。

    3、项目结构

    利用泛型封装的一些Base类,总代码不超过1000行,也是T-MVP的核心:
    ![T-MVP项目结构])X{FRT]K9SMSZ}CY.jpg](http:https://img.haomeiwen.com/i751860/281b2b0198b49042.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

    T-MVP利用泛型解藕和高度抽象封装之后,相较于传统MVC和MVP代码量大大精简,架构的代码量也是精简至极:

    例如BasePresenter的全部代码:

    BasePresenter的全部代码

    二、T-MVP的目标

    //TODO
    
    • 1 整理Base类,吸纳意见之后,继续优化封装,将简洁进行到底。让MVP不再背负繁杂的名声。
    • 2 发布到github
    • 3 争取发布到jcenter

    正在做:

    • 1 添加持久层数据存储realm

    • 2 recyclerview支持多种LayoutManager (GridLayoutManager StaggeredGridLayoutManager),支持多头部,多列表类型,多底部。

    • 3 添加fragment的MVP,同时也解决了一个页面需要多个P的问题

    • 4 尽量把反射去掉,免得有人抓住把柄说性能不好

    • 5 把框架部分隔离出来,单独做成Library

    • 6 用apt自动帮我们生成一些黑科技代码,达到事半功倍的效果

    三、进度

    • 1登录、注册
    • 2列表分类
    • 3文章详情 文章评论列表
    • 4用户中心 用户评论列表
    • 5更换头像
    • 6用户列表

    项目截图:

    7247fc46jw1f3p5r5tsynj20u01hcwl0.jpg 7247fc46jw1f3p5r8cjjhj20u01hc47t.jpg 7247fc46jw1f3p5r35jqnj20u01hcdt4.jpg 7247fc46jw1f3p5rceufqj20u01hcnaz.jpg

    相关文章

      网友评论

      • 灰灰coder:Rxjava 的Flowable 在哪里设置背压模式?在你的项目好像找不到你在哪里设置的
      • Austen_Yang:请教一个问题:
        在TRecyclerView的item中处理点击事件应该怎样处理。
        或者说我有一个需求是点击item中的某个view,去请网络应该怎样去处理??
        在线等。。。
        North_2016:@Austen_Yang databinding加个点击事件不难吧
      • 那一抹无言的芯痛:博主。AspectJ在gradle升级到3.0之后就不好使了。你有研究过么
      • Iverson毅:博主你好,请问你在使用Aspectj中,发布正式签名包时,有没有遇到Aspectj失效的问题
        Iverson毅:@North_2016 没有混淆,我打正式包没开启混淆,debug下可以正常使用,签名release后没开启混淆就失效了
        North_2016:@Iverson毅 混淆了吧
      • Zomll:楼主,,,请问把后来把model 去掉是处于什么考虑???

        别潜水,,快粗来!!!!!!!
        North_2016: @Zomll MODEL的定义很广泛啊,不一定需要固定的形态,数据层都可以叫M
      • 代码界的泥石流:一直想用白大神的,但是剥离不出来。。。总是莫名的报错 有木有初始化 没业务代码的Demo 求三爷赐教
      • longzekai:down了楼主的项目下来看了,发现里面的东西看的不是很明白,一些类库不明白是干什么用的,能不能出个说明文档?多谢了。
      • Poseidon_Wang:作者你这设计模式好复杂,项目结构优点复杂啊 你这边mvp是在p里面进行绑定的么rxmanager.add具体做了什么呢
      • cjcj125125:看的我一脸懵比,是不是法力不够
      • 1dadac33649b:大神 这个模式适合版本迭代较快的项目吗
      • 4f0a11dd431f:Error:Execution failed for task ':app:clean'.
        > Unable to delete file: D:\T-MVP-master\app\build\intermediates\exploded-aar\com.android.databinding\adapters\1.2.1\jars\classes.jar

        请问这个问题怎么解决?把 app文件夹下的build文件夹删除也不行
        4f0a11dd431f:@North_2016 问题解决了,先禁用AS的Instant Run功能 然后再将build文件夹删除就可以了
        North_2016: @Dzerzhinsky mac下没遇到过,win下好像有人偶尔遇到过,应该是as缓存问题的bug
        North_2016: @Dzerzhinsky 无法clean?被占用了吧
      • 飞起来的大雨:你这篇文章要是教学对于那些是懂非懂的人不够详细 ,你要把你用的每一个知识点 都要说清楚 着重强调
        North_2016: @飞起来的小糖豆 不是面向初学者😬
      • 3a9fc111d61c:博主,没有找到这个import com.data.repository.InstanceFactory
        North_2016:@混沌开发 http://www.jianshu.com/p/dca3e2c8608a
        3a9fc111d61c:@North_2016 谢谢博主,问题解决了,我想知道这是如何实现的,有相关的文章链接么
        North_2016: @混沌开发 Build &MarkProject
      • heheheqin:到guihub下载了 不懂怎么导入到Android studio 报错挺多的
        North_2016: @heheheqin 建议先把工具和环境都先更新到最新
      • marine8888:你好,我使用LeanCloud的appId和appKey,怎么就连不上呢,用postman来调试,提示未认证。
        {
        "code": 401,
        "error": "Unauthorized."
        }
        是什么原因啊?
        marine8888:@North_2016 需要另外开什么权限吗
        North_2016: @marine8888 没开权限吧
      • aef9b848dcba:作者对mvp理解很深,想请教一个问题,为什么要去掉dagger呢?是因为有反射的存在么?
        aef9b848dcba:@North_2016 恩,谢谢
        North_2016: @睡着什么都不怕 太麻烦,不适合小项目用
      • 那个死胖子:楼主好!这几天我一直在学习你的代码,收获很大,非常感谢,但是我发现你的RxBus文件里面的93行if判断语句写错啦,应该是if(!isEmpty(subjects))。
        North_2016: @那个死胖子 感谢提醒😂
      • 9f9bc9c61076:请问博主,这个框架在实际项目中实践过吗?
      • 969d0562a67a:楼主 你好 我想问一下 滑动RecyclerView 对应的ToolBar可以随着进行显示和隐藏 这个是怎么做到的
        North_2016:@超级qad 看代码(⊙o⊙)
      • 王永迪:这个mvp框架还是不错的,赞~
        不过有几个问题
        1、数据持久层,应该在model层来处理,可是博主这样设计数据持久化怎么办?
        2、p与m层的创建是在v层创建的,就是说v持有了m、p的引用,这样的耦合可否给个合理的解释?
        王永迪:@North_2016 你的样例里完全没有持久化的操作,v中创建了m,您能说这不是引用?v已经持有了m的引用了,只是你没使用而已
        North_2016:@walid 持久化就是在m层,创建并无引用
      • f98156cd8678:作者你好,看了你的T-MVP,觉得非常不错,clone 了项目在学习,昨天和朋友聊天中,推荐了你的T-MVP项目,然后朋友问我是怎么解决内存泄漏的. 说他看了一个项目是解决内存泄漏的.然后我去看 了,在P层 把View 作为 (weakrefrence)弱引用来持有. 然后我就想了一下T-Mvp 是怎么解决内存泄漏的. 大概思路如下,不知道正不正确,希望您点评一下.

        我举个例子. 打开 activity ,加载500条数据, 这时候View调用调用P层.loadData ,P层 loadData 方法里面调用RxMannge.add(Model.getDataFromNetWork) 就是把model的这个方法作为一个observable 放进了RxManage ,这个时候,用户迫不及待的关闭了activity ,但是网络请求还在继续,但是在activity ondestory 的时候,调用了 P 层的clear 方法,把observable 取消注册了,过了5秒之后, 网络请求成功,这时候model把结果给 P层, P层拿到这个observable , 不会做任何操作,因为这个observable 已经被unsubcribe了. 然后 activity 被GC,Model 被GC,P层被GC ,这样就避免了内存泄漏.因为我们model 层 始终是以observable 来返回的
        f98156cd8678:@North_2016 作者你好,我想问一下,每个P层都要有一个单独的RxMannge,那就会有很多RxMange,我想问,这样内存占用能控制好吗?
        North_2016:@灰太狼卡布达 是的,所有的异步操作都是在统一管理的,退出时统一取消订阅
      • 0fbd3d13a248:lz ,一直在关注你的框架,rxbus OnEvent 订阅第一个参数怎么使用,还请指点~
        0fbd3d13a248:@North_2016 谢谢lz的回帖;
        OnEvent(Observable<?> mObservable, Action1<Object> mAction1) {
        第一个参数 就是rxbus中,register注册事件源返回的Observable吗?
        North_2016: @Jerome_n 第一个参数就是一个int类型的tag
      • cc8367cdc9fa:楼主你好, Observable<Data<Image>> getallImage,这个访问网络返回不应该是 Observable<List<Image>>这样吗,我自己写的时候,放data报解析错误,这不应该放List《Image》吗,这个弄不明白
        North_2016: @放慢心跳111 不是,你可以打印数据看结构
      • 罗罗罗罗罗sky:首页列表点击详情 Caused by: java.lang.reflect.InvocationTargetException会出现这个异常
      • fef3a2360375:挺不错的,感谢分享。
        大家可以参考下下面项目dagger2+mvp的框架,希望支持下
        https://github.com/CarlLu/MVPframe
      • ad582e85962b: :smile: 感觉要学的好多, 谢谢博主 - -. 虽然没看懂
      • KingJA:对MVP来说,代码精简不是最主要的目的,解耦才是。博主可以深入谈谈这点。
        34f28c9af0fd:解耦确实是目的。但是优化能提高效率。
      • ef727e3aaf69:遇到了问题,一个 Activity 里面 ViewPager 包含了两个相同的Fragment,布局是一个 TRecyclerView,我不想让第二个 Fragment里的 TRecyclverView 接收到 RxBus 发射的消息,这种需求怎么办
        ef727e3aaf69:@North_2016 逻辑操作是在另外一个界面.两个fragment都是不可见的,操作完成 RxBus 就发射数据,TRecyclerView 跟着响应,但是 两个 TRecyclerView 加载的数据是不一样的,我又不想在 TRecyclerView 加新的 RxManager.on

        North_2016:@CSnowStack 或者简单点 直接两个fragment页面切换的时候 加上不同的标志位 根据标志位判断是否是需要接受消息的地方
        North_2016:@CSnowStack 判断Fragment是否可见啊 可见才接收数据
      • 0a2868f025e0:你这是用RX来解耦的吧
        North_2016:@RichsJeson Rx只用来切换线程 RxBus用来模块间解耦
      • 追云_似梦:思路蛮不错,感谢分享。
      • cbe7593ec1cc:将请求网络的过程封装到了model中,虽然更好的分层了,但是请求返回成功和失败的一些判断还是得在p中处理,这样会不会显得更繁琐了
        North_2016:@天桥下算命少年 因为请求返回成功和失败基本上都需要View做相关的处理,所以必须放在P层,M和V不能直接互相调用,这也是MVP的宗旨啊
      • cbe7593ec1cc:如果是view展示的一些逻辑判断的代码都放到p中,展示的代码放到view中,如果页面展示的逻辑判断很多,就会导致v 和p中的接口过多,有没有更好的解决方式,求指导
        North_2016:@North_2016 设计View的接口时候,要考虑这个接口的必要性,View的接口只是为了传输数据而不是为了让Presenter来控制,同时也要注意Presenter只从Model中获取数据
        North_2016:@天桥下算命少年 举个别的大神的例子

        public interface IView{
        void startSomeActivity(); //错,Presenter不能控制View,只能设置数据
        void showTextView(); //错,Presenter不能控制View,只能设置数据
        void setActivityPrepareData(DownloadInfo info);//对
        void setViewAdapterData(List<Data> listData);//对
        void showToast(int resId);//错 ResourceId不是从Model层中获取,View选取哪个ResourceId来显示是View自己的责任
        }
        North_2016:@天桥下算命少年 V的原则是,如果事件需要P和M层的帮助,才需要将事件传给Presenter,否则不要传给Presenter,让View自己处理,这样才不会导致类似V->P->V->P->V这种传来传去的无用功,导致接口过多且繁琐
      • 2d67b4352c27:我看你作者大大的代码,发现如果说我在一个页面(p层)有一个操作,在另一个地方也有一个一模一样的操作(p),那么是不是就显得不能复用了呢。打个比方,我申请一个账号,我获取验证码,但是这个获取验证码有个监听TextWatcher,监听你手机号是否正确,只有正确的手机号才能点击。在另一个忘记密码里面也有同样的获取验证码,怎么搞呢,我的想法是把获取验证码的代码p层另外封出去做为这两个p的父类
        2d67b4352c27:@North_2016 感谢回复,明白了。
        North_2016:@陈序猿2016 1、 你举的这个监听手机号的例子,仅仅是个验证而已,完全不需要P去操作,V完全可以自己处理 2、你说的是部分操作的复用,不是整个P的复用,只能通过提取父类,或者封装到其他地方统一调用的方式去解决复用性的问题。
        2d67b4352c27:因为在传统的mvp里面是可以调用多个p的,但是在作者大大的代码里面如果我另外new一个p出来不就是打破了这个框架吗,so封装出一个父类,让子类调用,或者其他的方法,作者大大给我个解决方案吧,谢啦。
      • ae9064aaf8f8:不错啊,不过加上dagger2更舒服。感觉TRecycleView 里有加入太多view外的东西,扩展性不是很好。
        ae9064aaf8f8:@North_2016 确实如果不做测试,加入dagger2代码反而冗余
        North_2016:@FFFFFlandre 没有太强的欲望引入dagger2 Component的细化粒度不好控制 找不到最佳平衡点 分得太细了反而觉得复杂了许多
        North_2016:@FFFFFlandre 是的 如果做Lib的话 TRecycleView 没法更好的扩展 所以只能尽量把能想到的功能慢慢往里加 你有什么更好的建议吗
      • OneJun:lambda虽然很爽。可是给人阅读的时候就不太友好了……
        North_2016:@OneJun 基本都是常用接口啊
        OneJun:@North_2016 主要是看别人代码的时候,还得翻翻接口怎么定义的……
        North_2016:@OneJun 用之前都会有这样的顾虑,用之后完全是多虑,习惯了都一样啊 ,只用一天就习惯了
      • 迷途小书童nb:如果activity有多个presenter怎么搞?
        CH_DHY:之前我一直有此困惑,今天看到博主的文章,虽没有直接提到如何处理多个Presenter,但是我看到了博主文章中Contract部分中添加了Model,我才反应过来,为了复用代码,我想要将Presenter拆成足够细的粒度,再用多个Presenter,已达到复用代码的目的。而现在,博主将想要复用的代码已经转移到了Model中,所以直接组合继承多个Model便可以达到复用逻辑代码的目的,也就无需使用多个Presenter了。
        North_2016:@Adley 个人意见 把一个Activity拆分MVP已经拆分的够细了 通常一个P就够用了 没有必要引入多个P 实在需要多个P 可以考虑 用多个Fragment碎片 这样Fragment又可以分MVP 实在是细分不到尽头 还是怎么简单怎么来吧
      • 浮华染流年:博主能不能先不用rxAndroid来写一些实现啊,先用最普通的代码实现,新手看rxJava的时候看不懂....
        North_2016:@inred 可以改的 改成Callback
        inred:@North_2016 但是现在的项目没用到rxJava额。。博主这样没法改了。。。
        North_2016:@浮华染流年 rxJava是大势所趋 入门还是很简单的 建议还是学习一下吧 入门神贴:http://gank.io/post/560e15be2dca930e00da1083
      • 尛坏蛋:泛型用好了的确是一个锋利的杀器!
        尛坏蛋:@Euterpe 你可以先看看基础的概念,我觉得你可以自己试试手,写代码比空泛的文章来的实际
        Euterpe:@尛坏蛋 有没有比较好的泛型讲解文章 到现在还不了解泛型
      • 帅气小伙:快点整理好,大兄弟
      • 天之大任:git地址呢
        North_2016:@天之大任 正在整理到git,感谢关注

      本文标题:T-MVP:泛型深度解耦下的MVP大瘦身

      本文链接:https://www.haomeiwen.com/subject/qtkjrttx.html