在开发中遇到这样一个问题,在工作的project(以下简称Project A)中,用到了Fragmentation(以下简称Project B) 库的三个module,这个库是我在维护,平时更改库中的一些bug,需要先使用一段时间,再推到github,并发布到jcenter, 所以在我的工作项目中,是直接使用本地的代码,没有使用jcenter去引用它。
这就遇到一个问题,在Project A 中改完 Project B的三个module,在等到发布的时候,就需要手动把代码拷贝一份到Project B中。如何能让这个过程简单点呢?
有小伙伴想到了,可以使用软连接,这样改完工作项目中的这三个module,就不再重复复制代码了。如果是多人协作开发,就不行了。
把Project B 这三个module ,分别使用git管理,整个工程项目使用repo 来管理,这样不管是哪个project 都可以方便的使用这三个module,但是这样对Project B 的改动较大。如果Project B 发布后,Project A 想通过jcenter 来引用这三个module,也不是很方便。(这种方式改造完,基本上就是现在江湖上组件化的结构)
于是在想,使用repo 来管理不同的Project,repo 本质是来管理多个git的,所以是project还是model,没有差别。这样可以来通过repo 来管理这个
另外两个问题:
在Android studio中 一个project 如何使用另外一个project的module呢?
在本地没有 Project B,如何让Project A 自动通过jcenter来引用?
repo 本质是来管理多个git的,所以无论是project 还是module,只要用git 来管理,那么他们就可以被repo管理
折腾过Android 源码的同学,应该对这个很熟悉,可以直接去配置default.xml,然后按照自己的思路,就可以
创建bin 目录,并加入到环境变量
$ mkdir ~/bin
$ PATH=~/bin:$PATH
在bin 目录下,下载repo 工具
$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
$ chmod a+x ~/bin/repo
repo 是用python开发的工具, 来管理不同项目的git,需要通过一个配置文件default.xml,来告诉他有哪些项目,远程地址是什么,本地路径是什么,review 等等
<manifest>
<remote name="github" fetch="https://github.com/JantHsueh" />
<remote name="aliyun" fetch="https://code.aliyun.com/cmoon" />
<default remote="aliyun" revision="master" sync-j="4"/>
<project name="cm-android.git" path="xw" />
<project name="Fragmentation.git" path="Fragmentation" remote="github"/>
manifest>
remote 远程仓库地址配置,可以多个。
default配置默认远程仓库地址
project
更多参数配置,查看官方文档repo Manifest Format
# 初始化 repo default.xml 文件 会下载到 .repo/manifests/ 目录下
repo init -u https://github.com/anlory/manifest
# 根据配置,下载代码
repo sync
至此,repo就把这两个project 管理起来了,在项目中应该如何配置project直接的关系呢?
default.xml 在 .repo/manifests/ 目录下 ,为了在project 中 能方便的通过 default.xml 控制项目,所以创建default.xml 软连接,放在project中,并加入到.gitignore 中
在setting.gradle 中,指定 module 的路径,这样就可以指定到其他project 中的 module
include ':app', ':mvvm'
//设置全局变量,在setting.gralde 和 build.gradle 中设置环境变量是不一样的
gradle.ext.localFragmentation = false
//在default.xml 中判断,是否有对应的project,如果有,repo sync 后 本地就有对应的代码,就配置使用本地工程代码
def manifest = new XmlParser().parse("${rootProject.projectDir}/default.xml")
manifest.project.each {
it.attributes().each { k, v ->
// path 对应的值包含 Fragmentation 字段
if (k == 'path' && v.contains('Fragmentation')) {
gradle.ext.localFragmentation = true
//指定Fragmentation工程的路径,在本示例中,两个工程的目录是同级的
def path = '../Fragmentation'
include ':Fragmentation'
project(':Fragmentation').projectDir = new File(path)
include ':fragmentation'
project(':fragmentation').projectDir = new File(path, 'fragmentation')
include ':fragmentation_core'
project(':fragmentation_core').projectDir = new File(path, 'fragmentation_core')
include ':fragmentation_swipeback'
project(':fragmentation_swipeback').projectDir = new File(path, 'fragmentation_swipeback')
}
}
}
gradle.ext.xx 和rootProject.ext.xx的用法差不多,只是变量存放的位置不同
更多全局变量的使用,可参考这篇文章 gradle使用技巧之全局变量
这个就很简单了,根据上面设置的全局变量来判断
dependencies {
if (gradle.ext.localFragmentation) {
api project(':fragmentation')
api project(':fragmentation_swipeback')
} else {
api 'me.xuexuan:fragmentationx:1.0.6'
api 'me.xuexuan:fragmentationx-swipeback:1.0.6'
}
}
当然可以有更多的玩法:
下面代码是参考这里的,需要引用哪个项目,从gradle.properties的配置中获取。
def moduleVersions = new Properties()
def moduleVersionsFile = new File("${project.projectDir}/gradle.properties")
moduleVersions.load(moduleVersionsFile.newDataInputStream())
dependencies {
def projects = moduleVersions.stringPropertyNames()
rootProject.subprojects.each {
if (it.name != 'host') {
implementation it
projects.remove(it.name)
}
}
projects.each {
//为了方便引用,这里要求我们上传到 maven 的各模块的包遵循一定的规则,groupid 需要保持一致,
// artifactid 最好是以子 module 的 project.name 来命名,如果不是这样的规则,就需要自己修改 gradle 脚本啦
implementation "me.ailurus.repo:${it}:${moduleVersions.getProperty(it)}"
}
}
在我看来,上面这种方式,算是project 之间的模块化,由于 project 可以多module的原因,很容易在多个project里 使用空 application 项目来打包apk
参考:
使用 repo 组织 Android 工程
Gradle Repo:一个能管理多个Git仓库,又能快速切换分支的Gradle插件
用repo管理自己的仓库
将数据从settings.gradle传递到build.gradle
Android Studio在同一个窗口中打开多个Project【附效果图附源码】