青團(tuán)社業(yè)務(wù)發(fā)展愈發(fā)多樣,Android使用模塊化設(shè)計(jì)進(jìn)行業(yè)務(wù)的解耦,在代碼都解耦上我們已經(jīng)有一套方案,但在工程的模塊化設(shè)計(jì)上,還缺少一個(gè)成熟的方案。
在前期模式過程中,我們?cè)赾omponent_task模塊上率先進(jìn)行了嘗試,結(jié)合當(dāng)時(shí)的需求和場(chǎng)景,我們使用率git submodule和pin工程來實(shí)現(xiàn)task工程的管理。
- git submodule 將代碼都版本管理從主工程中抽離,實(shí)現(xiàn)在多個(gè)主工程中代碼的同步
- pin工程 細(xì)化業(yè)務(wù)粒度,實(shí)現(xiàn)不同flavor下的特定代碼實(shí)現(xiàn)
改設(shè)計(jì)上開發(fā)一段時(shí)間后,git submodule暴露了一些問題:
- 學(xué)習(xí)成本,需要學(xué)習(xí)git submodule的一些命令和思想,尤其是commit id的概念
- 代碼穩(wěn)定性不可靠,主工程對(duì)task的依賴實(shí)際是通過commit id進(jìn)行依賴,多人開發(fā)下會(huì)出現(xiàn)下拉代碼,commit id并不會(huì)自動(dòng)去更新本地的task工程,因此可能會(huì)導(dǎo)致未處理的情況下提交代碼覆蓋了線上的最新commit id
雖然在生產(chǎn)環(huán)境下會(huì)對(duì)commit id進(jìn)行校驗(yàn),但獨(dú)立主工程的版本管理,版本號(hào)的缺失,以及維護(hù)成本確實(shí)對(duì)開發(fā)不夠友好。
為了提高代碼穩(wěn)定性,設(shè)計(jì)了一套新的依賴管理,在效率上較submodule低一些,但代碼穩(wěn)定性有很大的保障,責(zé)任劃分也更加明確。
gradle中的依賴
gradle中主要有三種依賴類型
- module
- project
- file
module依賴最簡(jiǎn)潔,只需要將對(duì)應(yīng)的倉庫路徑添加進(jìn)去即可:
implementation 'com.google.code.gson:gson:2.8.5'
project需要配置setting和build文件

在開發(fā)期會(huì)傾向于使用project對(duì)業(yè)務(wù)模塊進(jìn)行開發(fā),在打包或者不需要開發(fā)的模塊使用module依賴方式,手動(dòng)配置需要改動(dòng)多個(gè)文件,效率低。
QDepend
一鍵切換依賴模式,確保生產(chǎn)環(huán)境aar版本依賴,開發(fā)環(huán)境動(dòng)態(tài)依賴
對(duì)整個(gè)切換邏輯進(jìn)行封裝,作為一個(gè)plugin添加到maven中,方便實(shí)用。

Android Stuido中傳統(tǒng)的多module工程結(jié)構(gòu)如下:
├── app
├── component_jobs
├── component_login
├── component_me
QDepend改造后的工程,將原有的module抽離,工程中只含有一個(gè)app主module,內(nèi)部提供maven依賴各個(gè)模塊
├── app
module作為單獨(dú)的component庫,有獨(dú)立的git管理,默認(rèn)的目錄結(jié)構(gòu)是與工程平級(jí)
├── DrinkWater
│ └── app
├── component_jobs
├── component_login
├── component_me
在切換maven依賴和本地project依賴時(shí),只需配置module_map.properties中的開關(guān)位即可
使用
在一個(gè)新的工程中,我們需要手動(dòng)適配QDepend插件,進(jìn)行如下操作:
- 在gradle/depend目錄下添加module_map.json
- 工程目錄下添加module_map.properties
- setting.gradle添加com.qtshe.setting插件
- app/build.gradle添加com.qtshe.depend插件
改造流程
1.添加module_map.json
module_map.json按照特定的數(shù)據(jù)格式配置,添加需要?jiǎng)討B(tài)控制的module
{
"libs": [
{
"name": "homepage",
"group": "com.qtshe.mobile.component",
"path": "../component_homepage"
},
{
"name": "me",
"group": "com.qtshe.mobile.component",
"path": "../component_me"
}
]
}
其中:
- name: 對(duì)應(yīng)的module project名
- group: 在maven中g(shù)roup字段
- path: 本地的module路徑

比如gson的依賴:
implementation 'com.google.code.gson:gson:2.8.5'
name:com.google.code.gson
group:gson
2.添加module_map.properties
module_map.properties作為動(dòng)態(tài)依賴的開關(guān),把需要的module name設(shè)置為true即可,注意必須在json中存在:
homepage = false
me = true
以上會(huì)對(duì)me模塊替換成本地依賴

3.setting.gradle修改
需要引入plugin,注意setting不屬于build script,所以要單獨(dú)添加classpath
include ':QtsCustomer'
buildscript {
repositories {
maven { url "http://xxxx/nexus/content/repositories/releases/" }
maven { url "http://xxxx/nexus/content/repositories/snapshots/" }
google()
jcenter()
}
dependencies {
classpath 'com.qtshe.mobile:dependencies:1.1.1-SNAPSHOT'
classpath 'com.google.code.gson:gson:2.8.5'
}
}
apply plugin:"com.qtshe.setting"

4.build.gradle
因?yàn)橐褂貌寮?,所以需要在根目錄的build.gradle中添加classpath
buildscript {
repositories {
maven { url "http://xxxx/nexus/content/repositories/releases/" }
maven { url "http://xxxx/nexus/content/repositories/snapshots/" }
google()
jcenter()
……
}
dependencies {
……
classpath 'com.qtshe.mobile:dependencies:1.1.1-SNAPSHOT'
classpath 'com.google.code.gson:gson:2.8.5'
}
}
同時(shí)在build.gradle中應(yīng)用插件
apply plugin: "com.qtshe.depend"

適配好后只需要修改module_map.properties就可以實(shí)現(xiàn)切換
依賴庫的管理
一個(gè)完整的maven依賴包含group,module,version三部分。
模塊化開發(fā)中規(guī)范如下:
- 所有都component都以com.qtshe.mobile.component作為group。
- module命名以flavor-module格式,如趣喝水的login模塊:drinkwater-login
- 開發(fā)版:DEV-SNAPSHOT;正式版:verison_name.xx 如趣喝水login:1.0.0.1
開發(fā)模式
開發(fā)階段會(huì)涉及到頻繁的修改代碼,本地開發(fā)時(shí)可以通過QDepend切換本地模式即可,但實(shí)際情況往往是多人協(xié)助開發(fā),以及提測(cè)時(shí)jenkins上不斷的修復(fù)bug更新代碼,因此開發(fā)模式可以將依賴改為snapshot模式,以下是趣喝水開發(fā)階段的component依賴:
implementation 'com.qtshe.mobile.component:drinkwater-common:1.0.6'
implementation ('com.qtshe.mobile.component:drinkwater-login:DEV-SNAPSHOT'){
exclude group:'com.qtshe.mobile.component', module:'drinkwater-common'
}
implementation ('com.qtshe.mobile.component:drinkwater-me:DEV-SNAPSHOT'){
exclude group:'com.qtshe.mobile.component', module:'drinkwater-common'
}
implementation ('com.qtshe.mobile.component:drinkwater-jobs:DEV-SNAPSHOT'){
exclude group:'com.qtshe.mobile.component', module:'drinkwater-common'
}
SNAPSHOT會(huì)在每次下拉代碼時(shí)拉取最新的依賴,保證協(xié)助開發(fā)的同步和打包機(jī)出包代碼的時(shí)效性
如何生成SNAPSHOT?
工程中已經(jīng)集成了nnaven_publish.gradle,在rootProject下執(zhí)行如下命令即可更新本地依賴的module到倉庫中:
./gradlew qpublish
該腳本需要結(jié)合component中的maven.properties文件:
# module的版本
MODULE_VERSION=1.0.6
# module的maven名
MODULE_ID=common
# 標(biāo)記不生成SNAPSHOT
IS_LIB=true
其中IS_LIB是為了剔除common等lib,IS_LIB標(biāo)記后不會(huì)生成SNAPSHOT,標(biāo)準(zhǔn)的component工程不需要添加。
為了保證snapshot刷新的時(shí)效性,工程配置了cacheChangingModulesFor刷新時(shí)間:
configurations.all{
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
實(shí)時(shí)刷新可能會(huì)影響編譯速度,不需要的話可以在rootProject的properties中修改標(biāo)志位:
# is refrsh snapshot
REFRESH_SNAPSHOT = false
正式環(huán)境
在提測(cè)接近結(jié)束時(shí),需要將更新過的component進(jìn)行發(fā)版。具體步驟如下:
1.版本需要指定版本號(hào),更新component下的maven.gradle文件:
# module的版本
MODULE_VERSION=1.0.0.1
# module的maven名
MODULE_ID=common
將MODULE_VERSION指定為最新版本,版本號(hào)的規(guī)則約定如下:
"${version_name}.x"
比如趣喝水初版版本為1.0.0,對(duì)應(yīng)的component版本號(hào)為:1.0.0.1到1.0.0.99,期間可能還存在代碼修改的情況,因此預(yù)留兩位用于更新
2.提交代碼到遠(yuǎn)程倉庫
將component的代碼更新到release/xxx上,xxx為指定的flavor,如release/drinkwater。
在QDepend中 component的release/xxx類似git flow中的master,可以添加權(quán)限,以及用于code review
3.ModuleToMaven上執(zhí)行打包命令
通過jenkins,選擇需要更新的component和flavor,然后build

4.更新工程的依賴
將項(xiàng)目中對(duì)component的依賴更新到固定版本:
implementation 'com.qtshe.mobile.component:drinkwater-login:1.0.0.1'
5.發(fā)版文檔更新
每個(gè)項(xiàng)目都有有自己的wiki文檔,開發(fā)負(fù)責(zé)人需要發(fā)版前進(jìn)行更新
