关于android:vivo官网App模块化开发方案ModularDevTool

36次阅读

共计 5589 个字符,预计需要花费 14 分钟才能阅读完成。

作者: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

子模块依赖解决的流程如下:

(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% 以上。

正文完
 0