原本庞大的项目组件化拆分后如果所有的 module 都放在主工程下会增加编译时间,降低开发效率。我们可以将每个 module 单独创建仓库去管理,主工程通过依赖远程 aar 的方式去依赖,直接依赖 aar 的方式可以大大降低编译时间,提高开发效率。
利用 Git 模块框架管理组件化工程的优势
- 用文件系统将代码隔离
- 功能模块可独立编译,最终可以聚合编译
- 可以自由组合自己需要的模块
- 加快编译速度
下面会介绍两种 Git 组件化部署的方式
- git submodule
- git subtree
git submodule
新建一个主工程 Componentization 并将其 push 到 Git 仓库,我这里以 Github 为例,Componentization 仓库地址为 https://github.com/isJoker/Componentization.git
再新建一个工程 Login,并在工程里面新建一个 login library,将其 push 到 Git 仓库,仓库地址为 https://github.com/isJoker/Login.git
我们目前要做的就是 Componentization 工程里面关联子工程 Login,并且Componentization 工程里面的 app 模块可以依赖 Login 工程里面的 login library
添加子模块到 Componentization 项目中可以使用如下命令
git submodule add https://github.com/isJoker/Login.git
你可以将仓库地址替换为你自己的仓库地址
Git 会在 Componentization 文件夹中将指定的项目仓库的内容复制到本地,并且会生成一个.gitmodules
文件去记录存储的 submodule 的情况,如下图所示:
其中 Login 文件夹就是上面我们 Login工程,.gitmodules
文件会保存每个submodule的信息,文件内容如下
[submodule "Login"]
path = Login
url = https://github.com/isJoker/Login.git
当我们在开发环境中如果需要改动 login 模块的代码,应该怎么做呢?
第一种方法:切换到Login工程,修改 login 模块代码,再打包成 aar ,Componentization 主工程依赖 aar 。这样做在开发阶段显然很繁琐低效。
第二种方法:我们已经将 login 模块作为 Componentization 的子模块,那么我们可以直接让编译器识别 login 模块为 library,在 Componentization 的 app 模块直接 依赖本地 login 模块,开发阶段可以直接修改在本地 login 模块来快速验证需求改动。第二种方法具体做法如下:
settings.gradle文件修改如下:
修改完成后同步下 Gradle ,你会发现 Login 项目里面的 login 模块被识别成 library 了,如下图:
此时在 Componentization 工程的 app 模块的 build.gradle 中添加对 login 模块的依赖,这里需要注意的是,依赖的 module 名是 login_lib
,因为在settings.gradle中include是login_lib,当然你也可以改为login,名称任意,只需要include和project()中的名称一致就行,app模块依赖的也需要是在settings.gradle中include的名字
implementation project(path: ':login_lib')
同步下 Gradle后可以快速验证 login 模块的改动
当 Login 工程中的 login 模块被其他人员修改了,在主工程(Componentization)想要同步 login 模块的修改应该如何操作呢?
当主工程中需要更新最新代码时可使用如下命令
git submodule update
因为有缓存,上面的 git 命令并不一定能更新成功。可以使用下面的命令直接从远端检测每个子模块的代码并重新全部获取代码
git submodule update --remote
如果是第一次下载带有子模块的工程,子模块中并没有代码,需要使用 git 命令拉取各个子模块的代码
git submodule update --init --recursive
如果需要完全删除子模块,并且重新拉取代码,使用如下 git 命令
git rm -r --cached <子模块名>
rm -rf .git/modules/<子模块名>
如果不使用此命令,拉取的代码依旧是本地 git 仓库缓存的内容
git subtree
上面我们详细的介绍了 git submodule 的使用,使用 submodule 的形式管理模块能够更好的管理业务逻辑与代码解耦。但是 submodule 也有个不灵活的地方:代码同步只能单向,只能从子模块同步到主项目。但是从代码的严谨性去看,submodule 的单向管理更加严谨,其使用顺序是现在子模块提交,其他包含此子模块的项目再手动更新。为了弥补 submodule 的这个不灵活的方式,Git 在之后提供了灵活的 subtree 的管理机制。
submodule 和 subtree 对比
submodule
- 允许仓库以 commit 的形式嵌入到其他仓库的子目录中
- 更新仓库需要 init 和 update
- 使用 .gitmodules 文件记录子模块的信息
- git submodule 有缓存信息问题,删除子模块稍微麻烦
submodule 的结构如下
subtree
- subtree 使用 merge 合并的形式嵌入到项目中
- 不存在上面 submodule 的问题
subtree 的结构如下
subtree 的使用
1.在主工程中利用 subtree 关联子项目
git subtree add --prefix <子模块在主工程的相对路径> <子模块项目的git地址> <分支> --squash
如:
git subtree add --prefix LoginProject https://github.com/isJoker/Login.git master --squash
- 子模块在主工程的相对路径:用来存放子模块内容的文件夹,LoginProject为文件夹名称,一般在项目的根目录新建
- --squash 的意思是把 subtree 的改动合并成一次 commit,这样就不用拉取子项目完整的历史记录
- --prefix 后面的空格可以用“=”代替
2.提交更改子项目
先在主工程中使用 commit 命令提交更改,再用如下 git 命令 push 到子项目
git subtree push --prefix <子模块在主工程的相对路径> <子模块项目的git地址> <分支>
如:
git subtree push --prefix LoginProject https://github.com/isJoker/Login.git master
git 会遍历主工程提交的所有 commit,然后找出针对 子模块 项目的更改,将更改记录提交到子模块项目的 git 服务器,并保留相关的提交记录到子模块项目 git 仓库
3.更新主工程中的子模块代码
在主工程中执行如下 git 命令:
git subtree pull --prefix <子模块在主工程的相对路径> <子模块项目的git地址> <分支> --squash
如:
git subtree pull --prefix LoginProject https://github.com/isJoker/Login.git master --squash