D项目Android模块化 VCS演进之路

16年8月底,公司新启动了一个D项目(代号),从敲下第一行代码到如今,刚好1个年头,我们已经在VCS上经历了多次调整。

第一阶段(2016.9 - 2017.2)

在这个阶段,开发同学是2个人,需求快速迭代,此时在一个仓库上开发,工作流简单,代码规范等约束成本非常低,这也是最舒适的一个阶段。

本地feature, 不推远程,rebase到dev,保持线性,需求变化很快,甚至经常在开发阶段要经常变动,此时线性的git tree的优势非常明显,快速定位到具体的提交,再执行后续的操作。

第二阶段(2017.2 - 2017.4)

在2月份时,团队成员增长到4个开发同学(后来在5月份变成5个),因为模块间没有一个明显的界线,所以在开发时可能会有意、无意的碰到其它模块的代码,merge、rebase解决冲突的次数明显上涨。

此外项目增长到一定规模,模块之间存在较严重的耦合,同时一些模块相对于稳定,一直存在于主工程并不合适。

更重要的是,任何一处改动都需要编译整个项目,每次编译的等待时间都足以让你怀疑人生。

模块化势在必行

Android官方提供一种模块化方案,即模块以Module被主工程依赖,第一阶段中,我们已经对一些模块或者Library进行抽离,但是还存在以下问题:

  • 1、 稳定的Lib作为Module存在并不合适,每次主工程的编译都会带动这些Lib的编译,导致编译效率降低,急需将这些Lib上传到maven,从project依赖转换为坐标依赖

  • 2、 一些模块存在于主工程,仅以package分离,我们需要将这些迁移到Module

改造后的架构:

module_v1

还不够!

  • 1、 即使在做了上面的工作后,依然无法解决模块的追溯问题,我们只能从整个仓库的git历史去追溯模块的commit,出现问题检索时,解决速度并不高效

  • 2、 鉴于Gradle的生命周期:同步时,会扫描整个工程.gradle文件并执行,即使我只编译某个子模块!
    虽然我们基于模块开发的效率已经大幅度提高,但是因为上述Gradle的特性,我们依然浪费了一些构建时间

我们决定一步到位,将子模块全部迁移到独立仓库中
每个模块保证有独立的追溯历史,模块测试放到独立的仓库里进行。

这样迁移后,项目架构如下:


module_v2

理想很丰满,现实很骨感

在迁移后,我们的工作流转变成:
开发feature时,会在对应子模块工程里开发,然后发布到maven,主工程依赖新的子模块坐标。

如上的工作流其实没有什么问题,但是:

  • 1、 因为我们App体量并没有足够的大,业务虽然没有16年时变动那么大,但是还是常有跨多模块的需求,这种情况下,我们多是在主工程来测试的,如果过程有问题,就需要子模块重新发布,主工程重新同步、编译,而Andorid的同步速度大家是知道,如果开发额不够顺利,上面的步骤会多次重复...

  • 2、 团队4个开发同学,但是算上主工程有8个业务模块,平均1个人对应2个模块,这种规模之下,把所以子模块都迁移到独立仓库后,维护成本真的很高,模块的完全解耦,让开发同学感到不适

  • 3、 检索代码麻烦,主工程因为是坐标依赖,想搜索模块的代码,或者搜索某个方法的调用链都成为非常麻烦的事,而打开对应模块工程去搜索,耗时且麻烦

  • 4、 很多情况我们需要一起切换分支或者一起checkout到某一个commit节点,之前在一个仓库时checkout即可,但现在多个仓库很头痛

以上导致一个比较严重的问题:代码没有很好的在可控范围内,Review代码,查找BUG等都变得困难。

第三阶段(2017.4 - 至今)

为了解决上面的痛点,我们找到2个东西: RepoGit Submodule

Repo

Repo是Google管理Android源码的工具,是基于Git之上的构建工具,可以看作是Git的Wrapper,它提供一些命令集来操作其关联的模块。它引入一个Repo的角色来管理各个仓库,这意味着主工程和子模块是同等地位的。

使用Repo的话,模块关系如下:

Repo

这样的话,开发方式和之前没有任何区别,依然是子模块发布maven,主工程坐标依赖,来保证主工程和子模块的版本关系。
Repo只是帮我们打包了多仓库的 检出、切换等Git命令,并没有很好的解决上面提到的问题,所以Repo目前并不能解决我们的痛点,现阶段并不适合我们。

Git Submodule:

Git Submodule是Git提供的功能,它允许你将一个 Git 仓库作为另一个 Git 仓库的子目录,它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。

在Submodule中,当子模块的仓库有新的commit时,主仓库就会产生一个该模块的SHA值改变,该SHA值纪录了子模块的对应commit,以此来连接宿主关系。

使用Git Submodule的话,模块关系如下:

Git Submodule

这个关系图太熟悉了,不正是Android标准模块化的关系图吗?!所以我们决定在这个看似很完美的方案上摸索。

在经过Git Submodule的改造后,架构图如下:

module_v3

在经历一段适应期后,会发现Git Submodule的设计还是非常智慧的,但不可否认,不经任何装饰的Git Submodule直接用到App的工作流中,还是挺坑的。

比如当你主工程检出分支到某个commit时,子模块并不跟随切换到对应模块,需要手动执行 submodule update命令来让子模块切换到对应commit,但是这些更新后的子模块会在游离的HEAD上,在该状态上的commit是没有意义的。

除此之外还有很多其它的坑,如果让团队成员每个人都搞懂这套繁琐的机制并不现实,同时多仓库的存在让Git flow也繁琐很多,所以为了解决这些问题,我提供了一个脚本命令工具:ggsm

ggsm

ggsm可以看做是Repo + Git Flow的集合,它提供一些命令,可以方便打包操作多仓库,目标是像操作一个Git仓库一样操作主工程以及其Submodules,而目前我们确实也做到了。

ggsm

ggsm是Git Submodule的Wrapper,它并不会影响我们平时的开发习惯,你依旧在某个仓库里add ,commit - 当你要开发某个feature时,start命令帮你把所有仓库都切换到新的feature分支,当子模块都开发完成后,主工程里提交对应子模块的SHA(类似上传maven,主工程依赖新的坐标),然后使用merge finish推送代码到远程仓库,最后通过mr命令提交MR。

可以看到 我们把Repo和Git Flow集成在一起了,这样团队成员对Git Submodule几乎是无感知的,同时git flow也规范化了。

可以做的更多

事实上,ggsm承载了更多的功能:

  • 1、 容错处理机制,检测模块commit的完整性

  • 2、 Git hooks,钩子的更新、安装都放到了ggsm内,这一过程透明,目前我们做了commit msg检查、代码规范检查

  • 3、CodeReview,目前我们把Merge Request也集成到ggsm里,这样开发同学完成某个feature后会自动通过GitLab API创建MR,发送邮件通知等

关于Repo和Git Submodule

如果你的项目各个模块非常独立,体量也比较大的话,Repo是非常好的管理工具,总之:

没有最好的方案,只有更适合的方案

软链接

我们使用了软链接,它将子模块的Module软链接带主工程内,从而可以使用Project依赖。
在开发跨模块的需求上Project依赖会让你的开发效率大幅度提高,对于单一模块的需求,我们打开对应的子模块直接在其内开发即可。


软链接

未来

我们所做的一切,都是为了在 高可维护性和开发舒适性 上找到一个更好的平衡点。

对待子模块,我们并不拘泥于一种形式,比如 MsgPay这2个子模块是比较稳定的,那么这2个模块仓库就完全不需要使用Git Submodule关联,使用坐标依赖。

模块足够独立或稳定的时候,Git Submodule是多余的,反而会拖慢集成主工程时的效率。

此外目前的阶段有足够的灵活性,未来某个模块足够稳定或独立时,rm -rf submodule就可以解除宿主Git Submodule关系,转变为坐标依赖即可。

目前来看当前的VCS以及模块化方案已经算是一个很好的平衡点,足以应对当前阶段的D项目。

未来我们也会继续在VCS、工作流、自动化等方面做更深入的探索。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,042评论 25 707
  • 开发中我们经常会遇到这样的情况:项目越来越大,一些通用的模块我们希望将他抽离出来作为单独的项目,以便其他项目也可以...
    feil0n9wan9阅读 19,797评论 0 24
  • 2017.4.14 回家终于有时间去小吃街一趟,一直人多,终于挤进来一趟了。
    陈如一阅读 280评论 0 0
  • 她 生于汉朝,逝于汉朝 却比她的朝代存在得更久 她在古墓里沉睡了几千年 她的朝代却早已灭亡 也许 她早已料到她的朝...
    三湘涟漪阅读 915评论 3 5
  • 随着生活节奏的加快和工作压力的加大,不少人的胃都处于亚健康状态,并不是自己想像的那样健康。得了胃病,一定要选择正规...
    东垣养生阅读 699评论 0 0