書接上文,上回提到 B 站Android團隊為了解決元件化後協作上的問題,已經采用了 大倉(monorepo)
的方案來組織代碼。
國内實踐大倉的團隊少之又少,更别提 Android 的大倉了,幾乎沒有來自其它團隊的可借鑒經驗。在這條路上,我們可以算作先行者。本文粗陋,文中所列思路不可能适用所有團隊,僅給同樣想實踐Android 大倉的人些許啟發。
一個标準的 Gradle 項目
首先回顧一下 Android 項目的組織方式。自從13年開始官方逐漸遷移到
Android Studio做為 IDE 後,
Android 項目的開發和編譯就綁在 Gradle上了。
一個
标準的 Gradle 項目
結構如下所示:
MyApp/
├── build.gradle
├── settings.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradle.properties
└── app
├── build.gradle
└── src
└── main
├── java
├── res
└── AndroidManifest.xml
通常,會有多個Gradle Module存在:
MyApp/
├── build.gradle
├── settings.gradle
├── app
│ ├── build.gradle
│ └── src
├── lib1
│ ├── build.gradle
│ └── src
└── lib2
├── build.gradle
└── src
其中
settings.gradle
會注冊所有的 Module
include ':app', ':lib1', ':lib2'
多倉庫
随業務的擴張,Module 數量會越來越多。遵循多數人實踐過的元件化的思路,按業務分倉庫存放便理所當然:
android group/
├── MyApp/
│ ├── build.gradle
│ └── settings.gradle
├── app1/
│ ├── build.gradle
│ └── settings.gradle
├── app2/
│ ├── build.gradle
│ └── settings.gradle
└── libs/
├── build.gradle
└── settings.gradle
每個倉庫都是一個
标準 Gradle 項目
,通過
publishing
插件将module 都上傳 aar(或者jar)到 maven私服(如
nexus)上,再在
MyApp/build.gradle
中以 maven 元件的形式依賴它們,最終打包成apk:
repositories {
maven {
name = "myRepo"
url = "http://myrepo.example.com/android"
}
}
dependencies {
implementation 'com.example.android:app-a:1.0.0'
implementation 'com.example.android:app-b:1.0.0'
implementation 'com.example.android:lib-a:1.1.0'
}
[圖檔上傳失敗...(image-7a44b2-1546505515555)] 此時的代碼組織方式便是上文中所述的
形态(可能許多團隊正處于目前階段)。
多倉到大倉
那麼,如何既能快速搭建出适用于 Android 的大倉,又能不影響目前的團隊協作流程,還要盡量避免遷移帶來的開發效率降低?
經過一段時間的深入研究 Gradle API,終于找到一個簡單快速的可行方案——
composite builds。
上面說過,其實每個倉庫都是按業務分離的标準的 Gradle 項目,那麼就可以通過Gradle 官方提供的一個直接引入其它項目的API——
includeBuild——快速将所有倉庫組織起來。
在
MyApp/settings.gradle
中直接引入其它 Gradle 項目:
includeBuild '../app1'
includeBuild '../app2'
includeBuild '../libs'
另外比較令人驚喜的是,如Gradle 官方所說,
includeBuild
會自動替換依賴
com.example.android:app-a
為有對應聲明的module,如
project(:app1:app-a)
。這樣仍然可以用原有的依賴寫法,什麼都不用改,基本沒有遷移工作量。
具體示例可以見官方示例項目:
https://github.com/gradle/gradle/tree/master/subprojects/docs/src/samples/compositeBuilds/hierarchical-multirepo而且,使用
includeBuild
能保留目前的開發流程不變,每個子產品仍釋出到 maven 上,不破壞既有協作流程。各個業務的開發,通過Android Studio 打開自己項目的目錄即可,可以說幾乎沒有開發效率的影響。
解決了各個項目合并問題,另外一個就是要保證大倉的可維護性。
上文講過,依然沿用之前分層的方式,按約定的檔案夾組織:
<root dir>
├── build.gradle
├── settings.gradle
├── app/
│ ├── app-a
│ │ ├── src
│ │ └── build.gradle
│ ├── app-b
│ │ ├── src
│ │ └── build.gradle
│ ├── build.gradle
│ └── settings.gradle
│
├── common/
│ ├── common-a
│ │ ├── src
│ │ └── build.gradle
│ ├── build.gradle
│ └── settings.gradle
│
├── framework/
│ ├── lib-a
│ │ ├── src
│ │ └── build.gradle
│ ├── build.gradle
│ └── settings.gradle
│
└── MyApp
├── src
└── build.gradle
各個業務方的代碼隻需要按層級對号入座即可。
對号入座的方式有很多種,最簡單的便是直接拷貝項目到對應目錄,而如果要保留原倉庫的送出記錄,則可以使用 git filter-branch 和 git update-index 等指令(略過不表╮( ̄▽ ̄)╭)。
收斂合并權限
人總是會犯錯的,為了不讓近百名開發成員在一個倉庫裡打架,我們需要在 gitlab 權限配置設定的基礎上,通過API實作自動化的方式來合并代碼。
每個檔案夾(或者子產品)可以添加一個配置檔案,如
OWNERS,業務團隊自己配置設定 owner和reviewer。
<root dir>
├── app
│ ├── OWNERS
│ └── app-a
│ └── OWNERS
├── common
│ ├── OWNERS
│ └── common-a
│ └── OWNERS
├── framework
│ ├── OWNERS
│ └── lib-a
│ └── OWNERS
└── MyApp
└── OWNERS
通過gitlab webhook及note api來實作一個自動化工具:
當開發人員送出一個新的 Merge Reqeust 時,工具找到有變更檔案夾的對應 owner 及 reviewer,提醒他來review 和操作合并;當他在MR中送出merge指令時,由自動化工具執行合并操作。
綜述
- 通過 Gradle 的
将所有分開的倉庫合并到一起,并按一定的目錄層級組織各個子項目。includeBuild
- 通過 gitlab 的 api 實作一個自動化工具來合并代碼。
通過上述兩個步驟,便打造了一個大倉雛形。然而這隻是大倉的第一步,未來還有很多工作呢…
關于大倉,本文隻簡單描述了一下方案大緻思路,行文粗糙,希望對想實踐大倉的你有所幫助,如有疑問歡迎評論留言。