前言
在開發中遇到這樣一個問題,在工作的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 來管理項目
repo 本質是來管理多個git的,所以無論是project 還是module,只要用git 來管理,那麼他們就可以被repo管理
1、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
2、創建default.xml
repo 是用python開發的工具, 來管理不同項目的git,需要通過一個配置文件default.xml,來告訴他有哪些項目,遠程地址是什麼,本地路徑是什麼,review 等等
<?xml version="1.0" encoding="UTF-8"?>
<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 遠程倉庫地址配置,可以多個。
- name: 名字,也用於子倉庫的 git remote 名稱(.git/config 裏的 remote)
- alias: 別名,可省略,建議設爲 origin, 設置了那麼子倉庫的 git remote 即爲此名,方便不同的 name 下可以最終設置生成相同的 remote 名稱
- fetch: 倉庫地址前綴,即 project 的倉庫地址爲: remote.fetch + project.name
- pushurl: 一般可省略,省略了則直接用 fetch
- review: Gerrit code review 的地址,如果沒有用 Gerrit 則不需要配置(也就不能用 repo upload 命令了)
- revision: 使用此 remote 的默認分支
default配置默認遠程倉庫地址
- remote :它的值 是上面remote 標籤 配置的name 值,表示遠程倉庫地址
- revision : 遠程分支
- sync-j : 使用線程數
project
- name : 遠程倉庫的項目名,配合remote標籤的fetch值,共同組成完整倉庫地址:remote.fetch + project.name
- path:工程在本地的存儲路徑
- remote :它的值 是上面remote 標籤 配置的name 值,表示遠程倉庫地址。如果沒有配置,使用default中的配置
更多參數配置,查看官方文檔repo Manifest Format
3、repo 初始化 和代碼下載
# 初始化 repo default.xml 文件 會下載到 .repo/manifests/ 目錄下
repo init -u https://github.com/anlory/manifest
# 根據配置,下載代碼
repo sync
至此,repo就把這兩個project 管理起來了,在項目中應該如何配置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的用法差不多,只是變量存放的位置不同
- 在build.gradle 中配置全局變量 使用 rootProject.ext.
- 在setting.gradle中配置全局變量使用 gradle.ext
更多全局變量的使用,可參考這篇文章 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
Gradle Repo:一個能管理多個Git倉庫,又能快速切換分支的Gradle插件