图1:抖音项目工程架构演进
组件化
在大型项目快速发展的过程中,要保证敏捷开发迭代的最大障碍就是快速膨胀的代码体积导致的编译效率问题,依赖关系复杂化问题,以及业务线代码冲突问题。
移动端项目可以类比后端项目中采用的微服务架构,要解决多业务线并行开发、并行测试问题,采用流水线式迭代开发,提高发版、集成、交付、提审、发布效率,结合分治思想技术选型上可以采用组件化的方案。
大部分小型项目,组件化仅仅做到代码分仓,使用 Cocoapods 的来管理组件依赖,就像抖音项目最初的工程形态。
但是对于几百号人、几十个业务线规模的大型项目,需要设计一套合理的组件分层架构,理清组件间依赖关系,需要 CI/CD 工具链支撑组件发版与集成,需要本地研发工具支撑本地代码同步、工程配置、依赖管理和效率优化。
流水线式迭代开发
流水线(pipeline)技术是指在程序执行时多条指令重叠进行操作的一种准并行实现技术,该技术可以充分提高资源的利用率,同时缩短产品的研发周期。对于客户端项目,流水线技术能很大程度满足敏捷开发迭代的节奏。
图2:抖音流水线式迭代发版
抖音工程架构演进
========
阶段一:抖音原始工程架构(Original architecture of project)
图3:抖音项目原始工程架构图
抖音项目一开始是单体架构+Cocoapods,业务代码、工程配置、资源文件全部放在一个大业务仓库。由 Podfile 文件描述第三方仓库的依赖版本。
图4:抖音项目原始工程架目录结构
阶段二:分离壳工程后的工程架构(After splitting of host shell pod)
图5:拆分壳工程后的工程架构
分离壳工程后,工程配置、部分系统资源、工程主入口被拆分到主宿主壳工程。
Podfile 拆分出版本依赖管理文件 Podfile.seer,由依赖管理平台进行各个版本的容器化管理,业务仓跟随宿主集成发版,打平依赖,解决版本依赖决议耗时问题。
大业务仓中的代码和资源被拆分到各个业务线的仓库下,由 podspec 文件描述内外依赖。业务线仓库增加 ModuleInterface subspec,存放对外接口,采用依赖注入方式实现接口隔离,初步建立接口层。
业务仓库之间规定只能依赖其他业务仓库的 ModuleInterface subspec,通过 lint 进行编译检查。
部分基础能力代码被拆分成基础仓库,跟第三方仓库一样独立发版。本地研发工具支持单仓开发和多仓开发,不参与代码修改的仓库通过二进制的方式进行链接。同时 CI 流程上也支持通过二进制打测试包,提高打包效率。
图6:抖音项目拆分壳工程后目录结构
壳工程
图7:壳工程抽象
为了满足一个工程同时支持多个项目、部分业务线功能复用、部分业务线中台化发展的需求,我们把所有业务线抽象成独立的 Pod,所有业务 Pod 必须通过宿主的壳工程进行集成发版。
壳工程包含了项目依赖的 Pod 信息描述,同时还包括工程的配置、部分系统级别的资源文件、工程主入口代码。基于多份宿主壳工程,一份代码可以打包出抖音、抖音极速版等项目。
同时,基于宿主壳工程,一些业务线可以通过自动化同步生成自己的子壳工程,实现业务线自己的 Example 工程,进行独立开发,比如有语音通话的 Example 工程,有工具的 Example 工程,有直播的 Example 工程等等。
图8:子壳工程配置同步同步
接口层
接口层顾名思义,只提供依赖的抽象接口,所有接口都是 protocol 协议声明。
接口层限制了所有其他依赖,类、枚举、 外部协议都采用前向声明,podspec 上只允许声明对 DI(依赖注入)框架的依赖。接口层满足封装、隔离和组合的原则。
-
业务层面对外封装了实现代码;
-
编译层面隔离了组件间依赖传递,减少头文件 import 嵌套提高编译缓存的命中率,对于 swift 业务组件,还能达到减少编译传递的问题;
-
架构层面声明抽象协议支持接口组合;
-
DI 容器框架同时支持 stateless DI 容器,也支持 stateful DI 容器。
依赖打平
-
采用 Cocoapods 本身自带的版本依赖决议进行版本分析会消耗大量的时间;
-
Podfile.lock 过于繁琐,可读性很差,难以解决 Podfile.lock 的冲突;
-
隐式依赖被动/不符合预期地升级,难以确定性地声明所有依赖,防止隐式依赖被升级;
-
依赖版本在 Podfile/Podfile.lock 重复声明,增加了解决冲突的成本;
-
Podfile.lock 参与依赖版本决议流程比较复杂,会出现不符合预期的情况。
图9:把版本管理和仓库源信息迁移到 Podfile.seer 文件
- hook 掉 Cocoapods 采用 podfile.lock 进行版本决议的逻辑,采用 Podfile.seer 文件直接描述所有组件的版本信息,打平依赖。
阶段三:单仓多组件工程架构(Multicomponents in single repo)
图10:拆分单仓多组件后的工程架构
采用单仓多组件后,每个业务线仓库支持添加 podspec 增加组件,实现更小粒度的二进制依赖。业务线仓库内划分业务实现层、业务接口层、服务层和基础层,都是通过集成方式发版。
新增的服务层主要存放公共的业务逻辑和通用服务,限制 UI,一是满足业务逻辑复用,二是满足子壳工程最小化二进制依赖。同时服务层的服务接口也达到隔离依赖传递的目的,在不同的宿主上,支持通过改变服务层实现替换后台能力或者底层能力。建立分层间的依赖准入规则,完善 lint 编译链接检查。
图11:单仓多组件目录结构
编译链接完备性校验
-
编译校验:分开编译各个 subspec,确保每个 subspec 的依赖是正确的(由于 subspec 没有编译隔离)
-
接口符号校验:校验当前接口组件(ModuleInterface)中符号是否完备的,以保证其他组件单独引用是否能正常使用。如 extern 声明的全局变量。
分层依赖准入规则:
-
高层依赖低层
-
实现依赖接口
-
接口层无依赖
-
前向声明优先
-
服务层去"UI"
以下动画展示了业务实现层和服务实现允许依赖的分层:
图12:组件依赖关系示意图动画
阶段四:Example 子壳工程架构(Subshell for bizcomponent in example project)
图13:子壳工程架构
每个业务仓从宿主同步工程配置构建子壳工程。增加 AWELaunchKit 为子壳工程提供运行时的基础能力。通过服务层提供业务间运行时共享的服务能力,满足代码复用和更小二进制依赖。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。
人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。
资源持续更新中,欢迎大家一起学习和探讨。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
.imgtp.com/2024/03/13/H4lCoPEF.jpg" />
最后
Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。
人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。
资源持续更新中,欢迎大家一起学习和探讨。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!