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
子模块依赖处理的流程如下:
(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%以上。
vivo官网App模块化开发方案-ModularDevTool的更多相关文章
- vivo官网APP全机型UI适配方案
vivo 互联网客户端团队- Xu Jie 日益新增的机型,给开发人员带来了很多的适配工作.代码能不能统一.apk能不能统一.物料如何选取.样式怎么展示等等都是困扰开发人员的问题,本方案就是介绍不同机 ...
- Unity官网针对IOS开发有比较好的建议
Unity官网针对IOS开发有比较好的建议,我总结了翻译如下,后面附上原文. 尽量控制定点数量(注意所谓顶点不是建模时的顶点,而是引擎渲染时的顶点.例如,模型一个顶点如果设置了2个法向,那么对引擎来说 ...
- vivo 官网资源包适配多场景的应用
本文介绍了资源包的概念及使用场景,同时对资源包的几种使用方案进行对比.通过本文,大家可以快速掌握资源包的使用方法,解决单一配置满足多场景.多样式的问题. 一.业务背景 随着官网项目的业务深入发展,单纯 ...
- 官网app下载更换成微信公众号二维码 测试
微信现在很火啊.公司官网原先提供的ios和andriod的app下载链接要求切换成微信公众号二维码.简单的替换,大家都说不需要测试直接上线.还是测了下. 1 验证所有与下载相关的信息都已去除. 包括下 ...
- Vue2.5开发去哪儿网App 首页开发
主页划 5 个组件,即 header icon swiper recommend weekend 一. header区域开发 1. 安装 stylus npm install stylus --s ...
- PHP 开发 APP 接口 学习笔记与总结 - APP 接口实例 [3] 首页 APP 接口开发方案 ② 读取缓存方式
以静态缓存为例. 修改 file.php line:11 去掉 path 参数(方便),加上缓存时间参数: public function cacheData($k,$v = '',$cacheTim ...
- PHP 开发 APP 接口 学习笔记与总结 - APP 接口实例 [2] 首页 APP 接口开发方案 ① 读取数据库方式
方案一:读取数据库方式 从数据库读取信息→封装→生成接口数据 应用场景: 数据时效性比较高的系统 方案二:读取缓存方式 从数据库获取信息(第一次设置缓存或缓存失效时)→封装(第一次设置缓存或缓存失效时 ...
- MVC模块化开发方案
核心: 主要利用MVC的区域功能,实现项目模块独立开发和调试. 目标: 各个模块以独立MVC应用程序存在,即模块可独立开发和调试. 动态注册各个模块路由. 一:新建解决方案目录结构 如图: 二:Eas ...
- PHP 开发 APP 接口 学习笔记与总结 - APP 接口实例 [4] 首页 APP 接口开发方案 ③ 定时读取缓存方式
用于 linux 执行 crontab 命令生成缓存的文件 crop.php <?php //让crontab 定时执行的脚本程序 require_once 'db.php'; require_ ...
- Spring boot 官网学习笔记 - 开发第一个Spring boot web应用程序(使用mvn执行、使用jar执行)
Creating the POM <?xml version="1.0" encoding="UTF-8"?> <project xmlns= ...
随机推荐
- 搭建K8S集群前置条件
搭建K8S集群 搭建k8s环境平台规划 单master集群 单个master节点,然后管理多个node节点 多master集群 多个master节点,管理多个node节点,同时中间多了一个负载均衡的过 ...
- linux如何删除多余网卡
ifconfig tunl0 down ip link delete tunl0
- Codeforces Round #833 (Div. 2)补题
Codeforces Round #833 (Div. 2) D. ConstructOR 知识点:高位和对低位无影响 一开始以为和广州的M一样,是数位dp,后来发现只要找到一个就行 果然无论什么时候 ...
- MySQL57 zip安装
引用:MySQL5.7的.zip文件的配置安装 由于MySQL5.7之后在javaEE中交互的端口发生了变化,而MySQL官网中5.6.5.7版本64位的只有.zip文件,而.zip文件不像直接下 ...
- Function源码解析与实践
作者:陈昌浩 1 导读 if-else-在代码中经常使用,听说可以通过Java 8的Function接口来消灭if-else-!Function接口是什么?如果通过Function接口接口消灭if-e ...
- 【JVM故障问题排查心得】「内存诊断系列」JVM内存与Kubernetes中pod的内存、容器的内存不一致所引发的OOMKilled问题总结(上)
背景介绍 在我们日常的工作当中,通常应用都会采用Kubernetes进行容器化部署,但是总是会出现一些问题,例如,JVM堆小于Docker容器中设置的内存大小和Kubernetes的内存大小,但是还是 ...
- Java开发学习(四十六)----MyBatisPlus新增语句之id生成策略控制及其简化配置
在前面有一篇博客:Java开发学习(四十一)----MyBatisPlus标准数据层(增删查改分页)开发,我们在新增的时候留了一个问题,就是新增成功后,主键ID是一个很长串的内容. 我们更想要的是按照 ...
- Mybatis-Plus 对 json 的存储使用支持
Mybatis-Plus 对 json 的存储使用支持 场景分析: 随着数据库对字段类型支持的多元化,json 类型的存储已成为多场景高频使用的字段类型.而 MySql.postgrpSql 等都支持 ...
- 记一次 .NET 某工控MES程序 崩溃分析
一:背景 1.讲故事 前几天有位朋友找到我,说他的程序出现了偶发性崩溃,已经抓到了dump文件,Windows事件日志显示的崩溃点在 clr.dll 中,让我帮忙看下是怎么回事,那到底怎么回事呢? 上 ...
- python-docx操作word文档详解
案例 官网地址: https://python-docx.readthedocs.io/en/latest/ pip install python-docx from docx import Docu ...