2

vivo官网App模块化开发方案-ModularDevTool - vivo互联网技术

 1 year ago
source link: https://www.cnblogs.com/vivotech/p/17109727.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

vivo官网App模块化开发方案-ModularDevTool

作者:vivo 互联网客户端团队- Wang Zhenyu

本文主要讲述了Android客户端模块化开发的痛点及解决方案,详细讲解了方案的实现思路和具体实现方法。

说明:本工具基于vivo互联网客户端团队内部开源的编译管理工具开发。

现在客户端的业务越来越多,大部分客户端工程都采用模块化的开发模式,也就是根据业务分成多个模块进行开发,提高团队效率。例如我们vivo官网现在的整体架构如下图,分为13个模块,每个模块是一个独立代码仓。

(注:为什么这么分,可以参考之前的一篇文章《Android模块化开发实践》)

图片

完全隔离的代码仓,使每个模块更独立,更易于代码管理,但也带来了一些问题

图片

1、开发阶段,子仓开发以及集成开发调试,操作麻烦、易出错、难跟踪回溯

1.1、当开发时涉及的模块较多时,需要手动一个一个拉代码,多个子仓的代码操作非常麻烦,并且需要打开多个AndroidStudio进行开发;

1.2、子仓集成到主仓开发调试,有两种方式,但是都有比较大的缺点:

(1)方式1,子仓通过maven依赖,这种方式需要不断的发布子仓的snapshot,主仓再更新snapshot,效率较低;

(2)方式2,子仓通过代码依赖,也就是需要在主仓的settings.gradle中,手动include拉到本地的子仓代码,然后在build.gradle中配置dependencies,配置繁琐,容易出错;

1.3、主仓对子仓的依赖,如果是部分maven依赖、部分代码依赖,容易出现代码冲突;

1.4、apk集成的子模块aar和代码,没有对应关系,排查问题时很难回溯。 

2、版本发布阶段,流程繁琐,过多重复劳动,流程如下:

2.1、逐个修改子仓的版本,指定snapshot或release;

2.2、每个子仓需要提交修改版本号的代码到git;

2.3、每个子仓都要手动触发发布maven仓;

2.4、更新主仓对子仓依赖的版本;

2.5、构建Apk;

2.6、如果用持续集成系统CI,则每个子仓都需要配置一个项目,再逐个启动子仓的编译,等子仓全部编译完再启动主仓编译。

针对上述问题,我们优化的思路也很明确了,就是以自动化的方式解决繁琐和重复的操作。最终开发了ModularDevTool,实现以下功能:

图片

1、开发阶段

1.1、在主仓中,管理所有子仓代码(拉代码、切分支及其他git操作),管理子仓相关信息(代码仓路径、分支、版本等);

1.2、只需要打开一个AS工程,即可进行所有仓的代码开发;

1.3、对子仓的两种依赖方式(代码依赖和maven依赖)一键切换,支持混合依赖(即部分仓代码依赖,部分仓maven依赖);

1.4、编译时输出子模块的版本及对应commitid,便于回溯跟踪代码。

2、版本发布阶段

2.1、只需要在主仓修改子仓版本号,子仓无需修改,省去子仓代码修改和提交代码过程;

2.2、CI上只要配一个主仓项目,实现一键编译,包括子仓编译aar(按依赖关系顺序编译)、上传maven、编apk;

2.3、CI上支持3种编译模式:

  • OnlyApp:即只编译主仓代码生成apk(前提是子模块已发布maven);

  • publishSnapshot:即子仓编译上传snapshot版本,然后编译主仓生成apk;

  • publishRelease:即子仓编译上传release版本,然后编译主仓生成apk。

四、ModularDevTool概览

工具采用了shell脚本+gradle插件的方式实现的。

首先看下工程目录概览

图片

1、submodules目录是用来存放子仓代码的,子仓代码就是正常的工程结构,submodules目录如下图:

图片

2、repositories.xml文件是用来配置子仓信息的,包括模块名、代码仓、分支、版本等,具体内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<repositories>
<!-- 一个repository表示一个仓库,一个仓库下可能会有多个module -->
<repository>
<!-- 仓库名称,可以随意定义,主要用于本地快速识别 -->
<name>lib模块</name>
<!-- 上传至maven时的groupid -->
<group>com.vivo.space.lib</group>
<!-- 配置仓库中的所有子模块,如果多个module就添加多个module标签 -->
<modules>
<module>
<!-- 上传至maven时的artifactid -->
<artifactid>vivospace_lib</artifactid>
<!-- 上传至maven时的版本号 -->
<version>5.9.8.0-SNAPSHOT</version>
<!-- 编译顺序优先级,越小优先级越高 -->
<priority>0</priority>
</module>
</modules>
<!-- 注意仓库地址中的个人ssh名称要使用$user占位符代替 -->
<repo>ssh://$user@smartgit:xxxx/VivoCode/xxxx_lib</repo>
<!-- 开发分支,脚本用来自动切换到该分支 -->
<devbranch>feature_5.9.0.0_xxx_dev</devbranch>
<!-- 打release包时必须强制指定commitId,保证取到指定代码  -->
<commitid>cbd4xxxxxx69d1</commitid>
</repository>
<!-- 多个仓库就添加多个repository -->
...
</repositories>

3、vsub.sh脚本是工具各种功能的入口,比如:

  • ./vsub.sh sync:拉取所有子模块代码,代码存放在主工程下的submodules目录中

  • ./vsub.sh publish:一键编译所有子仓,并发布aar到maven

4、subbuild目录用来输出子仓的git提交记录,subError目录用来输出子仓编译异常时的log。

五、关键功能实现

ModularDevTool主要功能分为两类,一类是代码管理,用于批量处理git操作;第二类是项目构建,实现了动态配置子模块依赖、子模块发布等功能。

5.1 代码管理

vsub.sh脚本中封装了常用的git命令,用于批量处理子仓的git操作,实现逻辑相对简单,利用shell脚本将git命令封装起来。

图片

比如 ./vsub.sh -pull的实现逻辑,首先是cd进入submodules目录(submodules目录存放了所有子仓代码),然后遍历进入子仓目录执行git pull --rebase命令,从而实现一个命令完成对所有子仓的相同git操作,实现逻辑如下:

<!-- ./vsub.sh -pull代码逻辑 -->
cd submodules
path=$currPath
files=$(ls $path)
for fileName in $files
do
if [ ! -d $fileName ]
then
continue
fi
cd $fileName
echo -e "\033[33mEntering $fileName\033[0m"
git pull --rebase
cd ..
done

5.2 项目构建

(1)Sync 功能

通过执行./vsub.sh sync命令将所有子模块的代码拉取到主工程的submodules目录中。

Sync命令有3个功能:

1)如果子仓代码未拉取,则拉取代码,并切换到repositories.xml中配置的devbranch;

2)如果子仓代码已拉取,则切换到repositories.xml中配置的devbranch;

3)考虑到在一些场景(比如jenkins构建),使用分支检出代码可能会存在异常,在sync命令后面加 -c 参数,则会使用repositories.xml中配置的commitid检出指定分支代码。

Sync流程如下:

图片

(2)子模块依赖处理

在之前我们依赖不同子仓的代码时,需要手动修改settings.gradle导入子模块,然后修改build.gradle中的dependencies,如下图。

<!-- settings.gradle -->
include ':app',':module_name_1',':module_name_2',':module_name_3'...
project(':module_name_1').projectDir = new File('E:/AndroidCode/module_name_1/code/')
project(':module_name_2').projectDir = new File('E:/AndroidCode/module_name_2/code/')
project(':module_name_3').projectDir = new File('E:/AndroidCode/module_name_3/code/')
...
<!-- build.gradle -->
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
// 业务子模块 begin
api project (':module_name_1')
api project (':module_name_2')
api project (':module_name_3')
// 业务子模块 end
}
...

 团队中每个人代码的存放位置不同,在新版本拉完代码后都需要手动配置一番,比较繁琐。

基于sync功能已经把所有的子仓代码都拉到了submodules目录中,现在我们项目在构建时只需简单配置local.properties即可(local.properties配置如下图),确定哪些子模块是代码依赖,哪些子模块是maven依赖。

<!-- 其中key module_name_x表示子模块名,value 0表示maven依赖,1表示代码依赖,默认是maven依赖,也就是,如果不配置某些子模块则默认maven依赖 -->
module_name_1=0
module_name_2=0
module_name_3=1
module_name_4=1
module_name_5=1
module_name_6=1

子模块依赖处理的流程如下:

98a833e5951f73cd3bbb5717b7622a81.png

(3)publish功能

通过执行./vsub.sh publish命令实现一键编译所有子模块aar并上传maven。

publish命令主要有4个功能:

1)如果子仓代码未拉取,则自动拉取子仓代码;

2)如果是发布snapshot版本,则切换到devbranch分支最新代码,version中包含snapshot字符串的子模块,编译生成aar并上传maven;否则,则直接跳过,不会编译;

3)如果是发布release版本(即指定-a参数),则切换到commitid对应的代码,编译生成release版本的aar,并上传maven;

4)子仓的编译上传顺序根据配置的priority优先级来执行。

注:上述的devbranch、version、commitid、priority等都是repositories.xml中的配置项。

publish发布子模块的流程如下:

图片

六、ModularDevTool接入

接入本方案的前提是项目采用多代码仓的方式进行模块化开发。具体接入步骤也比较简单。

第一步,主仓依赖gradle插件modular_dev_plugin;

(该插件包含settings、tools、base、publish四个子插件,其中settings、tools和base插件配合实现子仓代码管理、动态依赖处理,publish插件实现子仓的aar发布)

第二步,主仓的settings.gradle应用settings插件,主仓的app build.gradle中应用tools和base插件;

第三步,主仓根目录添加repositories.xml配置文件和vsub脚本;

第四步,子仓依赖modular_dev_plugin,并应用publish插件;

第五步,中间层的子仓(比如App→Shop→Lib,那Shop就是中间层子仓)对下一层子仓的依赖版本号改成占位符,项目构建时会自动替换成repositories.xml中的版本号。如下图:

dependencies {
// 对lib仓的依赖,原来是依赖具体的版本号,现在改成“unified”占位符,项目构建时会自动替换成repositories.xml中的版本号
api "com.vivo.space.lib:vivospace_lib:unified"
}

 至此,ModularDevTool就接入完成了。

七、现在的开发流程

 基于这个工具,现在我们官网的开发流程如下:

第一步是clone主App仓代码,checkout对应开发分支,并在AndroidStudio打开工程;

第二步是修改repositories.xml配置,需要进行开发的子仓,修改devbranch为对应开发分支,修改version为对应版本号;

图片

第三步,通过./vsub.sh sync命令,检出所有子模块代码;

第四步,修改local.properties中子仓依赖的模式(maven依赖or代码依赖),修改完成后点击Sync一下,然后就可以正常进行代码开发了,开发体验与单工程多module模式完全一样。

 这个工具已经很成熟,在vivo钱包、vivo官网等项目已经使用多年,通过该工具,开发阶段,实现多业务模块集成式开发,解决代码仓分散管理和手动配置依赖等繁琐操作,发布阶段,实现多种编译模式以及一键编包能力,对于团队的开发效率有很大提升,支撑官网app项目3+业务线并行迭代,并且代码冲突降低50%以上。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK