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

news2024/10/6 12:19:39

作者: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%以上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Jqgrid分页勾选保存】三步实现表格分页勾选(取消勾选)保存(附源码)

目录1、创建临时存储数组&#xff0c;初始化赋值2、单行选中与取消&#xff0c;调整数组3、全选与取消全选&#xff0c;调整数组4、输出数组保存5、片尾彩蛋【写在前面】表格可以说是在我们的web页面中是最常见的&#xff0c;之前我们介绍过layui表格翻页勾选的实现过程&#x…

到2030年,边缘计算潜在市场将增长至4450亿美元!

国际电信咨询公司STL Partners近日出了一份边缘计算关键数据统计&#xff0c;重点介绍了九项边缘计算统计数据&#xff0c;边小缘着手翻译了一下这些数据&#xff0c;这些数据预测显示了边缘计算市场的增长潜力&#xff0c;以及边缘部署数量最多的垂直行业和地区。1.到2030年&a…

java Spring aop入门准备工作

首先 Spring 框架一般都是基于 Aspect]实现 AOP 操作 然后就会带出问题 什么是 Aspect 首先 Aspect并不属于Spring 他是一个单独的AOP框架 离开Spring他也能单独运行 但在Spring开发中 我们常用他来配合Spring完成AOP操作 所以说 我们是要 基于Aspect去配合Spring完成AOP操作…

压力应变电桥信号隔离放大变送器差分输入0-±10mV/0-±20mV转0-20mA/0-10v

概述&#xff1a;DIN11 IPO 压力应变桥信号处理系列隔离放大器是一种将差分输入信号隔离放大、转换成按比例输出的直流信号导轨安装变送模块。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等行业。此系列模块内部嵌入了一个高效微功率的电源&#xff0c;向输入…

ChatGPT入门案例|商务智能对话客服(二)

ChatGPT是人工智能研究实验室OpenAI新推出的一种人工智能技术驱动的自然语言处理工具&#xff0c;使用了Transformer神经网络架构&#xff0c;也是GPT-3.5架构&#xff0c;这是一种用于处理序列数据的模型&#xff0c;拥有语言理解和文本生成能力&#xff0c;尤其是它会通过连接…

day41【代码随想录】动态规划之01背包问题

文章目录前言 01背包一、二维dp数组01背包1.1 确定dp数组以及下标的含义1.2 确定递推公式1.3 初始化1.4 遍历顺序1.5推导dp数组1.6 完整代码二、一维dp数组01背包&#xff08;滚动数组&#xff09;2.1 确定dp数组以及下标的含义2.2 确定递推公式2.3 初始化2.4 遍历顺序&#xf…

移动应用开发环境搭建Andriod Studio

文章目录提示&#xff1a;虚拟化的开启零 java环境准备一 下载和安装Android Studio1.1 默认方式安装操作1.2 自定义安装方式1.3 StartService 失败问题解决二 第一个程序2.1 创建一个新项目2.2 下载和创建模拟器2.3 启动模拟器2.4 运行提示&#xff1a;虚拟化的开启 记得提前…

大神之路-起始篇 | 第17章.计算机科学导论之【计算理论】学习笔记

欢迎关注「全栈工程师修炼指南」公众号点击 &#x1f447; 下方卡片 即可关注我哟!设为「星标⭐」每天带你 基础入门 到 进阶实践 再到 放弃学习&#xff01;涉及 企业运维、网络安全、应用开发、物联网、人工智能、大数据 学习知识“ 花开堪折直须折&#xff0c;莫待无花空折…

2023年浙江水利水电施工安全员精选真题题库及答案

百分百题库提供水利水电施工安全员考试试题、水利水电施工安全员考试预测题、水利水电施工安全员考试真题、水利水电施工安全员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 119.下列关于大模板按照的说法正确的是&#x…

#459 津津有味:北方人对饺子的痴迷可能是刻进骨子里的

点击文末“阅读原文”即可收听本期节目剪辑、音频 / 卷圈 编辑 / SandLiu 卷圈 监制 / 姝琦 文案 / 粒粒 产品统筹 / bobo 录音间 / 声湃轩活着不端饺子碗&#xff0c;哭天抹泪没人管。你一定见识过铺天盖地的对“北方人一过节就吃饺子”的调侃。但饺子就是很好吃这件事&am…

ARM uboot 源码分析4 -启动第二阶段

一、start_armboot 函数简介 1、一个很长的函数 (1) 这个函数在 uboot/lib_arm/board.c 的第 444 行开始到 908 行结束。 (2) 450 行还不是全部&#xff0c;因为里面还调用了别的函数。 (3)为什么这么长的函数&#xff0c;怎么不分成两三个函数&#xff1f;主要因为这个函数…

100种思维模型之非sr思维模型-012

什么是sr? sr是stimulus-response的缩写&#xff0c;意思是刺激反应。 那么非sr思维模型就是非刺激反应思维模型的意思。 今天我们来聊聊非sr思维模型——一个提醒我们思考&#xff0c;提醒我们任何时刻都有选择权的思维模型。 本文依然从三个方面进行介绍&#xff0c;何谓…

你是真的“C”——详解结构体知识点

你是真的“C”——详解结构体知识点&#x1f60e;前言&#x1f64c;什么是结构体&#xff1f;&#x1f64c;1. 结构体的声明&#x1f64c;1.1 结构的基础知识1.2 结构的声明1.3 结构成员的类型1.4 结构体变量的定义和初始化2. 结构体成员的访问&#x1f64c;3结构体传参&#x…

推荐领域新人必看书籍:《推荐系统实践》

这本书非常适合推荐领域的新手&#xff0c;因为这本书的主要目的更接近于科普&#xff0c;而不是描述具体的推荐算法。什么是推荐系统&#xff1f;如果有一位你喜欢的女士约你一起外出&#xff0c;肯定不需要别人推荐你是否赴约吧&#xff01;&#xff08;信息量太小则不需要被…

VS Code中的GIT操作

一、前言 我们在进行项目开发时都免不了与GIT打交道&#xff0c;但是面对各种的难记的GIT命令总是手足无措&#xff1b;还好编译器中内置了GIT的仓库的一系列操作&#xff0c;掌握了可视化的操作就不用担心记不住GIT命令符了。下面主要介绍VS Code中具体的操作&#xff1a; 二…

【安全】Nginx实现反向代理负载均衡

基础概念 什么是负载均衡&#xff1f; 负载均衡用于从“upstream”模块定义的后端服务器列表中选取一台服务器接受用户的请求&#xff1b;即把请求均匀的分摊给上游的应用服务器。最基本的配置方式便是轮询&#xff1a; 负载均衡策略 策略 轮询 根据请求顺序分配 weight …

【软件工程】COMP5241 SE课程笔记

Software EngineeringCourse1 IntroductionCloud Native AppsScheduleSoftware InstallProject Chaos ReportWhat is Software EngineeringHow to define a good AppsSteps of SoftwareCourse4本笔记记录始于2023年2月13日&#xff0c;为在读研究生期间COMP5241 SE课程笔记整合…

检测脸部情绪有多难?10行代码就可以搞定!

引言面部表情展示人类内心的情感。它们帮助我们识别一个人是愤怒、悲伤、快乐还是正常。医学研究人员也使用面部情绪来检测和了解一个人的心理健康。人工智能在识别一个人的情绪方面可以发挥很大的作用。在卷积神经网络的帮助下&#xff0c;我们可以根据一个人的图像或实时视频…

封装、继承、多态、上下转型、静态绑定、动态绑定、PO/Bean/Vo/Do/Dto,dljd reyco郭

封装 “封装”这个概念&#xff0c;由两部分构成&#xff1a;一部分是封&#xff0c;一部分是装。“封装”这个动作&#xff0c;顺序应该是先装后封。 装&#xff1a;原本name、age、score是3个不同的、离散的数据&#xff0c;它们之间是有关系的是&#xff0c;都是用来描述一个…

东芝TLP5772光耦与SLM346兼容光耦的单通道隔离驱动器比较

东芝TLP5772光耦与SLM346兼容光耦的单通道隔离驱动器比较一般描述&#xff1a;SLM346是一款光兼容单通道&#xff0c;隔离栅驱动器&#xff0c;用于IGBT、MOSFET和2.5A源和2.5A汇峰值输出电流和5kVRMS加强隔离等级。SLM346可以驱动低侧和高侧功率场效应晶体管。可靠性升级超过标…