mvp关联activity生命周期_浅谈项目架构重构:组件化与MVP

为什么要进行局部架构重构

解决完全局架构的优化,整个项目已经实现模块化,然而这样就足够了吗?来看一看模块内部是什么样的,先看一下相对于简单的模块:

af675275c31c6b8e3ec908825ffd63be.png

看起来还挺干净利落的对吧?采用mvc模式,一个activity承载一个业务。那么当业务变得复杂且需要与其他activity共用又会变成什么样?以我们项目中相册业务为一个例子。

fa90495f01d95bd2b203d3f0e312b652.png

可以看到,这个层次结构比上面的复杂了一些,在我们的项目中,有几个业务具有相册业务,为了实现可复用性,我们抽取了共用的相册业务Activity,提供一些相册相关的基础业务和操作。由于校园业务的相册又分视频相册和图片相册,而这两个相册又只有点击和界面的些许不同,大部分逻辑相同,于是又抽取了校园相册的基础Activity。这是这个复杂业务的大致情况,实际为了复用,这里并不止抽取了这几个Activity,大概是一个不同业务就抽了一个共用的Activity,导致相册这一块的代码逻辑和结构层次极其复杂,混乱。

抽取共用的Activity确实提高了复用度,但是当业务复杂起来,就带来了严重的冗余问题,同时业务的不合理拆分容易造成开发时的混乱。

再看看mvc这块,在图中可能看不出什么端倪,但实际上,在复杂的业务模型下,view与model之间往往纠缠不清,view持有多个model,多个model又持有view,同时大量的controller代码和view代码糅合在Activity中,不仅使得Activity的代码量剧增,难以维护,往往还带来了不少内存泄漏的问题。

为此,采用组件化来改善现有架构,并引入MVP模式优化业务逻辑的处理。

先来看看引入组件化+MVP的架构,能解决什么问题:

  • 实现代码上的解耦,减少冗余的Activity
  • 模块职责划分明显,层次清晰
  • 实现各通用功能的高复用性和灵活性
  • 通过组件组装的方式,使得整个模块层次结构分明
  • 组件可以作为独立工程开发,因此对功能的开发,测试不用再进行全项目编译
  • 解决在mvc模式容易因接口回调导致内存泄漏的问题

下面来看一下,组件化+MVP是什么样的架构。

什么是组件化与MVP

组件化:组件化模块化并不是同一个层次的概念,组件化的粒度更小,笔者更偏向于用Lib来形容一个组件。就比如我们常用的Picasso,这是一个图片加载组件,又如OkHttp是一个网络请求组件。这样子的单一功能或单一功能集的Lib,便是组件。

实现组件化即将项目中的业务功能进行细分,划分出粒度更小的通用功能组件,这些组件可以在多个项目中实现复用,而不再单单只归属于某一特定项目。

举个例子来说,笔者参与项目有个相册模块,看似抽离了很多通用的Activity,好像可以在其他项目中复用,可实际上呢?仅仅只有最上层的一个Activity可以实现复用,其他Activity都与View强耦合。而最上层的Activity功能又是最少的,根本无法实现复用,只能说减少了一点代码量。

对此,将整个相册模块抽离成了一个图片展示组件

08cbeafb83cfbfd01f4bd3bcb1c7ec15.png

这个组件依赖于图片加载组件,也就是Picasso之类的框架,实现了通用的图片列表,图片九宫图,图片添加删除等展示功能。原来所有冗余的Activity全部弃用,只需实现一个承载特定业务逻辑的界面,并使用该组件进行展示即可。

MVP:对于MVP模式相信大家都不陌生,MVP模式是传统安卓MVC模式的改良版,将原本承担View和Controller功能的Activity改良为只承担View这一功能,引入Presenter作为View与Model层的中介,架设特定的业务逻辑。

引用一张经典MVP的图片来解释:(原文)

a8d4c635aa156c377d43e7583d3a1789.png

对于MVP模式,网上包括Google官方都有提出几种不同的实现方法,常见的有todo-mvp与mvp-clean等,其实对于MVP的实现方式,不同人有不同的见解,不同方式也适用于不同的项目,因此只要理解MVP的思想,以最适用自己项目的方式实现即可。在本项目中主要以todo-mvp为原型进行改良,实现了一套自己的MVP模式

如何实现组件化和MVP

如何实现组件化

对于组件化开发,从功能的角度,笔者将组件分为两种:功能型组件View型组件。简单的来说,View型组件一般比功能型组件多了View层的实现。

功能型组件

  • 也就是日志组件、网络组件、图片加载组件等基础组件。这些组件一般不具备View,只负责提供逻辑功能,需要开发者自己实现特定的View来搭载这些功能。
  • 对于功能型组件的实现大家应该都比较熟悉,就算没有实现过也应该用过各种第三方的开源组件。

View型组件

  • 而对于View型组件也就是笔者项目中的图片展示组件以及知乎的Matisse组件。这样的组件不仅仅具有逻辑功能,还附带View的实现。
  • 对于知乎实现的Matisse组件,其采取的是Activity的方式来作为载体,暴露出该Activity的启动方法作为入口。这是大多数View组件会采取的实现方式。以Activity做为载体,那么实现过程与我们平时进行开发的过程差别不会很多,然而却不可避免的减少的了灵活性和复用性。并且只能通过继承来扩展其他功能,组件的通信也十分局限。这也正是笔者项目一开始的实现方式。

还有另外两种实现View型组件的方式,一种基于Fragment,另一个种基于Custom View。两种实现方式各有各有的优缺点。

基于Fragment的View组件

基于Fragment的实现类似于Matisse基于Activity的方式。一样的生命周期处理,同样可以套用MVP模式。实现过程与我们平时开发也不会差很多。最后只需要暴露Fragment的创建方式即可。

不同于Activity,采用Fragment使得整个组件灵活了许多。可以采用接口通信,并同时保留了与Activity相应的生命周期处理。很好的划分了各个组件之间的界限,解决了复用和耦合的问题。

不过,采用Fragment作为主体,那么不可避免的就带了一系列碎片化的问题,包括旋转屏幕、状态改变、Fragment的重建等问题。

基于Custom View组件

首先要说的是这类Custom View不同于常见的自定义View,常见的自定义View往往不具有太多的非View逻辑操作。这类Custom View以自定义View为载体,具备自己的Model层和Presenter层。可以说是另类的MVP模式。

以图片选择组件为例子,我们需要的东西有:图片加载组件(Picasso)、获取本地图片的工具类(Model)、承载这两者的Custom View、以及协调CustomView和Model的Presenter。这个Custom View可以继承自RecyclerView或者ViewGroup,不同父类保留给外界使用者的特性不同,根据不同情况可以选择不同父类(如果希望保持足够多的列表特性给外界,那么可以继承自RecyclerView,如果不希望外界对内部干预过多,那么继承自ViewGroup)。而统筹这三者逻辑类似于Matisse,只不过Matisse以Activity为承载。

最后我们只需要将这个Custom View暴露给外界即可,其余逻辑由内部实现。这样的实现方式避免了采用Fragment而带来的碎片化问题,也很好的解决了复用和耦合的问题。不过也因此Custom View的生命周期难以管理,无法与Activity对应上,需另行处理。

管理组件

对于组件化,其实实现难点并不多,更多是理解组件化的思想和如何管理各个组件。对于管理组件而言,尤其是View型组件,网上有很多方案,有不少人建议用Router统一管理,组件化使用Router,有些大材小用。

对于组件化的管理,采用上传到私有Nexus仓库,直接通过Gradle依赖管理。

如何实现MVP模式

对于MVP模式,网上已经有很多文章进行解释,GitHub也有很多案例,这里简单介绍一下思想,更多详细建议学习官方DEMO

在MVP模式中,将架构分为三层:

  • View
  • View层主要负责界面元素的展示,包括Button、TextView等。由XML和Activity / Fragment组成。前者主要负责静态界面布局,负责负责动态界面。
  • Model
  • Model层通常是MVP模式中最复杂的一层,主要负责数据的请求、读写。这一层往往可以分为本地数据和网络数据。
  • Presenter
  • Presenter是MVP模式中极其关键的一层,作为View和Model的管理者,也即是中介者。承载着相应的业务逻辑,接收来自View的请求,转换为对Model的请求,进而接收Model的回复,从而通知View进行刷新。

三个层次之间的关系如下

4f4b9b69c939ef2bb3cbd63d7d4c0b33.png
  1. View接收用户的触摸事件,传递action给Presenter
  2. Presenter接收到action,执行对应的业务逻辑。如果涉及到数据请求,那么发送相应的request给Model
  3. Model接收到request,进行本地或网络数据请求。待请求结束,返回对应的response给Presenter
  4. Presenter接收到Response,执行对应的业务逻辑。如果需要更新界面元素,那么通知View进行处理

整个流程看起来十分清晰,实现也不难,需要注意的一点是要注意各个层次的调用关系。View层不能主动调用Model,Model亦不可调用View层。两者需要通过Presener作为桥梁。

再思考

其实对于局部架构的重构,并没有太多的技巧,更多的是一种思想。

无论是组件化还是MVP,都是力求实现代码层次的解耦,实现更高程度的复用,是我们的代码更加优雅。

纵然使用组件化和MVP都会增加我们的代码量和研发周期(稍微,可接受)。然而,这样的付出是值得的,但我们回观整个架构,会感觉一切都是可控的,不管是控件的管理,还是View、Model的管理,都十分灵活。

最后再看一下结合模块化重构完之后,整个项目的架构:

2a2ed4d8f224cfc66043296a55ee8b23.png

一目了然,整个App划分为多个业务模块,这些业务模块以Module的形式管理。对于每个Module需要使用的通用功能,也就划分为了组件,这些组件以Lib的形式管理,藉由Gradle进行依赖管理。

而不管是模块亦或是组件,我们基本都采用了MVP的模式(部分简单的模块、组件除外)。这样便使得不管是整体架构,亦或是局部架构,都十分灵活可管理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值