为什么要进行区域性架构重构
解决完全域性架构的优化,整个专案已经实现模组化,然而这样就足够了吗?来看一看模组内部是什么样的,先看一下相对于简单的模组:看起来还挺干净利落的对吧?采用mvc模式,一个activity承载一个业务。那么当业务变得复杂且需要与其他activity共用又会变成什么样?以我们专案中相簿业务为一个例子。
可以看到,这个层次结构比上面的复杂了一些,在我们的专案中,有几个业务具有相簿业务,为了实现可复用性,我们抽取了共用的相簿业务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功能又是最少的,根本无法实现复用,只能说减少了一点程式码量。
对此,将整个相簿模组抽离成了一个图片展示元件
这个元件依赖于图片载入元件,也就是Picasso之类的框架,实现了通用的图片列表,图片九宫图,图片新增删除等展示功能。原来所有冗余的Activity全部弃用,只需实现一个承载特定业务逻辑的界面,并使用该元件进行展示即可。
MVP:对于MVP模式相信大家都不陌生,MVP模式是传统安卓MVC模式的改良版,将原本承担View和Controller功能的Activity改良为只承担View这一功能,引入Presenter作为View与Model层的中介,架设特定的业务逻辑。
引用一张经典MVP的图片来解释:(原文)
对于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进行重新整理。三个层次之间的关系如下:
View接收使用者的触控事件,传递action给PresenterPresenter接收到action,执行对应的业务逻辑。如果涉及到资料请求,那么传送相应的request给ModelModel接收到request,进行本地或网络资料请求。待请求结束,返回对应的response给PresenterPresenter接收到Response,执行对应的业务逻辑。如果需要更新界面元素,那么通知View进行处理整个流程看起来十分清晰,实现也不难,需要注意的一点是要注意各个层次的呼叫关系。View层不能主动呼叫Model,Model亦不可呼叫View层。两者需要通过Presener作为桥梁。
再思考
其实对于区域性架构的重构,并没有太多的技巧,更多的是一种思想。无论是元件化还是MVP,都是力求实现程式码层次的解耦,实现更高程度的复用,是我们的程式码更加优雅。
纵然使用元件化和MVP都会增加我们的程式码量和研发周期(稍微,可接受)。然而,这样的付出是值得的,但我们回观整个架构,会感觉一切都是可控的,不管是控制元件的管理,还是View、Model的管理,都十分灵活。
最后再看一下结合模组化重构完之后,整个专案的架构:
一目了然,整个App划分为多个业务模组,这些业务模组以Module的形式管理。对于每个Module需要使用的通用功能,也就划分为了元件,这些元件以Lib的形式管理,借由Gradle进行依赖管理。
而不管是模组亦或是元件,我们基本都采用了MVP的模式(部分简单的模组、元件除外)。这样便使得不管是整体架构,亦或是区域性架构,都十分灵活可管理。