現(xiàn)在的Android工程都是采用 gradle 來(lái)構(gòu)建的,從早期的單一工程架構(gòu)(一個(gè)項(xiàng)目只有一個(gè)主 module)晒衩,到現(xiàn)在的組件化架構(gòu)(一個(gè)項(xiàng)目包含有多個(gè)module)增淹,項(xiàng)目結(jié)構(gòu)越來(lái)越復(fù)雜膛堤,在這里陸續(xù)記錄一些挺有用的小技巧奋构。
1. resolutionStrategy 統(tǒng)一全局第三方庫(kù)版本
resolutionStrategy 顧名思義“解析策略”壳影,也就是說(shuō)可以在 gradle 解析各種依賴庫(kù)時(shí),配置一些解析策略弥臼。
1.1 resolutionStrategy.force
我們?cè)陂_(kāi)發(fā)的過(guò)程當(dāng)中宴咧,會(huì)依賴使用各種第三方庫(kù),比如說(shuō)最常見(jiàn)的 appcompat-v7
庫(kù)径缅,不僅我們自己的代碼里會(huì)依賴該庫(kù)掺栅,而且有可能我們依賴的第三方庫(kù)也會(huì)依賴該庫(kù),但是不同的地方依賴該庫(kù)的版本號(hào)都有可能不一致纳猪,這樣在編譯的時(shí)候氧卧,整個(gè)項(xiàng)目當(dāng)中會(huì)出現(xiàn)各種不同版本號(hào)的 appcompat-v7
庫(kù),編譯的時(shí)候可能會(huì)報(bào)錯(cuò)氏堤。我們不可能修改第三方庫(kù)里對(duì) appcompat-v7
庫(kù)依賴的版本號(hào)沙绝,那么我們可以通過(guò) resolutionStrategy.force
來(lái)強(qiáng)制編譯時(shí)統(tǒng)一庫(kù)的版本號(hào)。
android {
configurations.all {
resolutionStrategy {
force 'com.android.support:appcompat-v7:28.0.0'
force 'com.android.support:support-v4:28.0.0'
}
}
}
1.2 resolutionStrategy.dependencySubstitution
還有一種這樣的情形,假設(shè)一個(gè)第三方工具庫(kù)叫 org.gradle:util:3.0
宿饱,除了我們自己的項(xiàng)目以及部分第三方庫(kù)也有依賴它熏瞄,但是在實(shí)際使用的過(guò)程當(dāng)中脚祟,發(fā)現(xiàn)該庫(kù)有些功能不滿足或者有bug谬以,那這個(gè)時(shí)候該怎么辦呢?一是提 issue 讓該庫(kù)的作者去修改由桌,但是時(shí)間上來(lái)不及为黎;二是源碼拿過(guò)來(lái)本地進(jìn)行修改,直接本地集成行您,但是要修改依賴方式铭乾,那么這個(gè)時(shí)候可以這樣做:
假設(shè)我們本地 module 名稱叫 :util
android {
configurations.all {
resolutionStrategy {
//遠(yuǎn)程依賴替換成本地依賴
substitute module('org.gradle:util:3.0') with project(':util')
//也可以將遠(yuǎn)程依賴換成另外的遠(yuǎn)程依賴,假設(shè)我們修改過(guò)的代碼發(fā)布到自己的 maven 中央倉(cāng)庫(kù)后叫:com.xxx.xxx:util:3.0
//substitute module('org.gradle:util:3.0') with module('com.xxx.xxx:util:3.0')
}
}
}
2. gradle.settingsEvaluated 在編譯之前做一些初始化工作
之前接手過(guò)一個(gè)項(xiàng)目娃循,是采用 react-native 框架進(jìn)行開(kāi)發(fā)的炕檩,熟悉前端以及 React 的同學(xué)都知道,項(xiàng)目 build 的第一步是先進(jìn)行 npm install
或者 yarn
操作捌斧,這樣所有依賴的第三方庫(kù)都會(huì)下載到 node_modules
目錄下面笛质。在 react-native 當(dāng)中,Android 工程會(huì)依賴 node_modules
目錄下的原生 Android 代碼捞蚂,但是因?yàn)閹?kù)的版本關(guān)系妇押,每次我編譯的時(shí)候,都會(huì)發(fā)現(xiàn)某個(gè) module 中的 build.gradle 配置與主工程沖突姓迅,需要手動(dòng)修改才能編譯敲霍。需要注意的是,node_modules
目錄是動(dòng)態(tài)下載的第三方庫(kù)丁存,并不會(huì)被提交肩杈,也就是說(shuō)每次執(zhí)行 yarn
操作之后再編譯 Android,都需要手動(dòng)更改下這個(gè)目錄下的庫(kù)解寝,非常繁瑣扩然。那么有沒(méi)方法來(lái)自動(dòng)幫我們解決這個(gè)問(wèn)題,不用每次都要手動(dòng)修改呢编丘?答案是可以与学。gradle 有個(gè)生命周期鉤子方法 gradle.settingsEvaluated
,在 gradle configure 階段之前讓我們做一些處理嘉抓。
在我們項(xiàng)目的 settings.gradle
文件的最開(kāi)頭加上該方法的監(jiān)聽(tīng)索守,注意該方法必須加在 settings.gradle 文件中,在 build.gradle 里沒(méi)用抑片。以下是個(gè)范例:
gradle.settingsEvaluated {
println "-------------settingsEvaluated start------------"
//假設(shè)我們要修改的是 react-native-fs 這個(gè)庫(kù)當(dāng)中的 build.gradle 文件
//先刪除該文件
delete("${rootDir}/../node_modules/react-native-fs/android/build.gradle")
//我們將修改好的文件放到 ./replace-files/react-native-fs 這個(gè)目錄當(dāng)中
//前面已經(jīng)刪除了目標(biāo)文件卵佛,現(xiàn)在只需將我們需要的文件復(fù)制過(guò)去即可
copy {
from("./replace-files/react-native-fs/build.gradle")
into("${rootDir}/../node_modules/react-native-fs/android")
}
println "-------------settingsEvaluated end------------"
}
執(zhí)行打包命令 ./gradlew assembleRelease
等的時(shí)候,一開(kāi)始就會(huì)執(zhí)行以上代碼,自動(dòng)幫我們做好這些事情截汪。
除此之外疾牲,還可以做很多事情,比如打包前先刪除某個(gè)緩存目錄的文件衙解,每次打包時(shí)自動(dòng)遞增修改 versionCode
阳柔、versionName
,檢查 local.properties 配置文件是否存在已經(jīng)配置是否正確等等蚓峦。
3. 修改包的輸出路徑
android {
applicationVariants.all { variant ->
variant.outputs.each { output ->
def date = releaseTime()
//這里只改變 release 包的輸出路徑
if (variant.buildType.name.contains('release')) {
//我們定義包的輸出路徑為 android_outputs/Android_APK
//這里可以根據(jù)情況定義任意路徑
variant.packageApplicationProvider.get().outputDirectory = new File(project.rootDir.absolutePath + File.separator + "android_outputs"+ File.separator + "Android_APK")
//包名范例:Android-pro-release-v2.6.6-c266-d202108181428.apk
def fileName = "Android-${variant.productFlavors[0].name}-${buildType.name}-v" + versionName + "-c" + versionCode + "-d" + date + ".apk"
output.outputFileName = fileName
}
//拓展一下舌剂,也可以根據(jù) productFlavors 來(lái)做配置
/*
if (variant.getName() == "qaRelease") {
//這里還可以動(dòng)態(tài)修改 productFlavors 里的東西,例如:
variant.buildConfigField "String", "APP_CHANNEL", '"xiaomi"'
} else if (variant.getName() == "proRelease") {
}
*/
}
}
}
def releaseTime() {
return new Date().format("yyyyMMddHHmm")
}
4. 找到版本沖突的庫(kù)
當(dāng)項(xiàng)目越來(lái)越大暑椰,所使用的第三方庫(kù)越來(lái)越多的時(shí)候霍转,最讓人頭疼的是針對(duì)同一個(gè)庫(kù)引入了多個(gè)不同的版本,編譯的時(shí)候?qū)е聨?kù)沖突直接編譯失敗一汽,問(wèn)題找起來(lái)也很麻煩避消。還是利用resolutionStrategy
,我們可以快速找到?jīng)_突的庫(kù)以及各版本號(hào)召夹,配置如下:
configurations.all {
resolutionStrategy {
failOnVersionConflict()
}
}
下面是一個(gè)范例岩喷,在我的項(xiàng)目當(dāng)中,om.android.support:multidex
這個(gè)庫(kù)引入了2個(gè)不同的版本:1.0.3 和 1.0.2
Conflict(s) found for the following module(s):
- com.android.support:multidex between versions 1.0.3 and 1.0.2
找到?jīng)_突的庫(kù)及版本后戳鹅,我們可以用前面的方法均驶,強(qiáng)制指定統(tǒng)一的版本號(hào)以解決沖突。
持續(xù)更新中......
系列文章
Android Gradle學(xué)習(xí)(一):Gradle基礎(chǔ)入門
Android Gradle學(xué)習(xí)(二):如何創(chuàng)建Task
Android Gradle學(xué)習(xí)(三):Task進(jìn)階學(xué)習(xí)
Android Gradle學(xué)習(xí)(四):Project詳解
Android Gradle學(xué)習(xí)(五):Extension詳解
Android Gradle學(xué)習(xí)(六):NamedDomainObjectContainer詳解
Android Gradle學(xué)習(xí)(七):Gradle構(gòu)建生命周期
Android Gradle學(xué)習(xí)(八):統(tǒng)計(jì)Task執(zhí)行時(shí)長(zhǎng)
Android Gradle學(xué)習(xí)(九):一些有用的小技巧