安卓项目架构设计 - 梳理现有项目的混乱
架构设计通常发生在一个项目生长到一定程度时,结束了野蛮生长进入稳定期,回过头来审视整体项目的架构,大多是一个比较混乱的状态。本文将从现有项目出发,梳理项目中混乱的结构,讲述项目架构设计的过程。
一、目标与规划
1.1 完整的项目架构
对于一个成熟项目来说,项目架构中应该包含基础层、核心层、业务层、应用层、跨平台层共计 5 个层次。
![](https://static001.geekbang.org/infoq/ff/ff622a2c38284cb185791b72a7c3c3bf.jpeg?x-oss-process=image%2Fresize%2Cp_80%2Fauto-orient%2C1)
基础层包含一些最基本的能力。原则上应该是可以脱离公司特定的环境、不依赖其他非基础层模块、功能单一且健壮,比如异常检测、行为埋点、网络请求、图片加载、异步处理、全局 UI 等等,基础层大多可以引用三方库。
核心层包含一些可以在公司环境下跨 APP 使用的能力。原则上应该是业务层的主要支撑、不依赖其他非基础层和核心层模块、功能单一且健壮,比如 MVP 或 MVVM 框架、应用配置、版本控制、日志、监控、热修复、三方平台开放能力对接等等,核心层通常是自研或基于三方库包装或改造。
业务层包含一些比较独立、边界比较清楚的业务。在这一层开始会有 Activity,就会涉及到模块间页面跳转的问题,通常由一个独立的路由模块负责。业务模块原则上应该专注于单一的业务功能,比如用户登录、个人主页等等。
应用层则是 APP 主体项目,多个 APP 可以在应用层引用不同的业务模块,搭建自身的应用能力。
跨平台层应该根据团队的技术偏好进行选择,需要与否、技术方向都可以酌情考量。
1.2 项目演进规划
梳理现有项目 的整体架构是重中之重,每次重构我们都必须确保业务逻辑不应被影响,各个自研模块、依赖的三方库都应纳入考量。从项目中剥离出哪些功能进入基础层、核心层、业务层,现有模块中有哪些需要拆分、改造、删除,都需要在梳理完成后就列出来,保证后续的规划可以顺利推进。
重构的节奏 很重要,业务不可能停滞下来等待重构结果,业务与重构双线进行在最后合并时会有大量的冲突,重构的结果没有业务进行验证也增长了上线的风险。最好是小步快跑的模式,现在大多团队都是敏捷开发的业务模式,重构按小模块小功能进行,一点点和业务融合,在测试和上线时都可以控制风险。
三方库 可以减少很多开发的工作量,但在选择三方库时要注意几个原则:维护团队和社区比较大,遇到问题后能够有足够多的自助解决空间;底层功能强大,支撑尽可能多的上层应用场景;拓展能力灵活,支持在框架基础上做能力拓展和 AOP 处理;Api 侧友好,降低上层的理解和使用成本。
基础层 应该是最先搭建起来的,从梳理结果中将基础模块陆续剥离出来,由于核心层和业务层都强依赖基础层,所以一个完善的基础层可以加快核心层和业务层搭建的速度。重构的过程中,每次抽离基础层应该会修改大量上层代码,在上层模块比较少甚至只有一个的时候,改动会方便很多。
核心层 在基础层完成后进行搭建,主要目的就是让业务层更好更快的完成一个业务的开发,将业务无关的、可在多个业务复用的功能封装成模块。核心层抽离时对上层的改动会小一些,但由于紧靠业务层,改动时必须很谨慎,容易对业务逻辑造成影响。
业务层 是所谓模块化的表现,主要目的是方便团队协同开发,在大型复杂项目中跨业务组做需求可以减少冲突。如果团队规模较小,业务耦合比较高,也可以考虑不拆分。
跨平台层 和业务层不同,它本身就是比较独立的部分,拆分也简单,不拆分影响也不大。
二、制定重构计划
2.1 项目梳理
如果项目已经是有多个模块,那么第一步就是梳理所有模块的能力,明确每个模块内存在的资源文件、UI 组件。梳理完模块,再梳理应用的所有功能、UI 组件、资源文件。这一步是最耗费时间的,一定要仔细挖掘所有代码,否则在拆分模块的时候就是大坑,到时再来修改方案就比较费时费力了。
把梳理出的所有功能、UI 组件、资源文件,按各个业务进行归类,不能归属到具体业务的算做应用层,先把各个业务理清楚,暂时不管是否可以拆成核心模块和基础模块。
业务梳理完会得到一个应用层和业务层的项目结构,这时候开始从应用层到业务层重新梳理,筛选出可以拆成核心模块的部分。这时候要注意是否存在相似甚至完全一致的功能,可以考虑合并成一个功能或者是一系列功能。
现在我们有了应用层、业务层和核心层,已经比较复杂了,注意要耐心仔细。再重新从应用层->业务层->核心层进行梳理,独立出基础模块。注意 UI 组件、全局样式比较容易被忽略,有一个统一的 UI 模块会方便上层搭建 UI 界面,减少资源冗余。
2.2 拆分模块安排
梳理完成后,我们有了一个现有项目可拆分模块的梳理表,整体架构从应用层->业务层->核心层->基础层该拆成什么样应该已经很明确了。
现在可以安排拆分的模块,这时应该自底向上开始拆分,把基础模块一个个搭建出来,小步快跑的模式。刚开始拆分出基础模块时,上层的改动会比较大,动辄数百个改动都是常见的,一定要谨小慎微。我们把原本只包含一个应用层的项目,向下抽取了一个包含网络库、图片加载库和 UI 库等众多原子能力库的基础层。这样做之后,对于协同开发、整包构建和代码复用都起到了很大的改善作用。
![](https://static001.geekbang.org/infoq/cb/cbd48071aa84e05f17bc83db38b6d4b3.jpeg?x-oss-process=image%2Fresize%2Cp_80%2Fauto-orient%2C1)
基础层搭建完成后,可以开始拆分核心层。从全局视角来看,基础层和核心层也能作为一个整体,共同支撑上层业务。这里将其分为两层,主要考虑到前者是必选项,是整体架构的必要组成部分;后者是可选项,但同时也是衡量一个 App 中台能力的核心指标。
![](https://static001.geekbang.org/infoq/7f/7f95a6bf73a72ca4cb71fb37e11c6a3d.jpeg?x-oss-process=image%2Fresize%2Cp_80%2Fauto-orient%2C1)
有了核心层就可以对业务层进行拆分了,通常一个迭代会有多个需求,一个需求通常是在一个独立的业务中的,通常很难保证迭代里的所有需求都能按时交付,如果没有独立的业务模块,要么整体延期要么就得进行痛苦的分离工作。如果有业务层,那么只需要不将延期的业务模块更新到应用层,整体迭代发版还是可以顺利进行的。
![](https://static001.geekbang.org/infoq/48/485bfd7bacc74f63f2393af02b059808.jpeg?x-oss-process=image%2Fresize%2Cp_80%2Fauto-orient%2C1)
如果为了在一些业务需求上减少开发工作量,让一份代码同时在安卓和 IOS 上运行,单人开发也降低了需求沟通的成本,引入了跨平台的能力,那么就可以从应用层再抽离出一个跨平台层。
![](https://static001.geekbang.org/infoq/ff/ff622a2c38284cb185791b72a7c3c3bf.jpeg?x-oss-process=image%2Fresize%2Cp_80%2Fauto-orient%2C1)
至此,一个相对完整的端侧系统架构已经初具雏形了。后续业务上会继续有着更多的迭代,但项目的整体结构基本都不会偏离太多,更多的是针对于当前架构中的某些节点做更深层次的改进和完善。
评论