- 說在前面:Gradle中project是非常重要的俱恶,所以也會有非常多的API及其可配置的屬性,筆者也有許多不了解的范舀,在這只是講一些開發(fā)中比較常用的一些API和屬性合是。但是了解了這些,其它的自己去看锭环,去查資料相信也是可以搞懂的聪全。當(dāng)遇到了一些需求是以下API或者屬性配置不能解決的,可以到Project文檔中查查有沒有可以幫助到自己的API田藐。其它想了解的在這也可以查閱荔烧。另外,Android Plugin DSL Reference中可查閱android閉包中有哪些可配置選項(xiàng)汽久。共勉
一鹤竭、Project概念
- 什么是Project
- 前面說過,每個(gè)module都會對應(yīng)著一個(gè)Project景醇。先將項(xiàng)目切換到project面板
- 我們也知道臀稚,初始化階段,會從settings.gradle中解析生成Project
- 可以看到settings.gradle中include了app三痰、annottations(小手一抖多了個(gè)t,注意一下就好annotations)吧寺、compiler窜管、core、ec,那么我們的項(xiàng)目中有幾個(gè)Project對象呢稚机?是5個(gè)嗎幕帆?下面我們執(zhí)行projects這個(gè)Task看一下我們的項(xiàng)目中有幾個(gè)Project
- 很明顯,有個(gè)根project 'ecommoerce'赖条,在它的下面有5個(gè)子Project,所以一共有6個(gè)Project失乾。Gradle以樹的形式管理Project,最外層有一個(gè)根Project,在它下面有其它的幾個(gè)子Proect。Gradle是根據(jù)目錄中有沒有build.gradle文件來判斷這個(gè)目錄是不是一個(gè)Project,我們可以看到在根目錄中有一個(gè)build.gradle文件纬乍,在每個(gè)子module中也有它們各自的build.gradle文件碱茁。而每個(gè)Project都是在build.gradle中去配置和管理的,這些build.gradle最終會被Gradle編譯為Project字節(jié)碼仿贬。
- Project的作用
根Project是用來統(tǒng)籌管理所有的子Project的纽竣,而每個(gè)子Project都對應(yīng)了一個(gè)輸出。比如我們的app module的類型是application的茧泪,那么它最終就對應(yīng)生成了一個(gè)APK文件,是android library類型的蜓氨,最終會生成一個(gè)aar文件,java library類型的生成一個(gè)jar文件等等
二队伟、Project核心API
Gradle生命周期API
在Gradle生命周期及其監(jiān)聽中其實(shí)已經(jīng)用過了语盈,里面的this其實(shí)就是project,直接用project替換this也是可以的缰泡。這一部分的API是用來監(jiān)聽Gradle的生命周期的。project相關(guān)API
這一部分主要是在每個(gè)project中獲取自己父Project和子Project的代嗤,為每個(gè)Project提供操作父Project和管理子Project的能力
-
獲取當(dāng)前project及其子project
- 注意上面的方法是寫在根Project的build.gradle中的棘钞,若是在子module中的build.gradle編寫,則無法得到下面的結(jié)果(模仿執(zhí)行projects Task的結(jié)果)干毅,可能只輸出了一個(gè)“Root project:[所在的module ]”,因?yàn)閜roject的getAllProject方法是將當(dāng)前project當(dāng)成root project宜猜,并且獲取當(dāng)前project的所有子project。
-
獲取根project
- 注意它們之間的區(qū)別
- 獲取project的子project
//調(diào)用自己定義的方法硝逢,方法中調(diào)用了gradle提供的API getSubprojects()
this.getSubProjects()
def getSubProjects() {
//this.rootProject 獲取項(xiàng)目的根目錄姨拥,
// getSubprojects()方法返回所有子Project的Set集合,遍歷
def subProjectSet = this.rootProject.getSubprojects()
subProjectSet.eachWithIndex {
Project project, int index ->
if (index > 0 && index < projectSet.size() - 1) {
println "+--- Project ':$project.name'"
} else {
println "\\--- Project ':$project.name'"
}
}
}
//在終端執(zhí)行g(shù)radlew clean渠鸽,以下是結(jié)果
'''
> Configure project :app
\--- Project ':annottations'
+--- Project ':app'
+--- Project ':compiler'
+--- Project ':core'
\--- Project ':ec'
'''
- 順帶說一下:因?yàn)閳?zhí)行clean Task叫乌,會走Gradle的生命周期,而配置階段是需要解析所有Project的所有Task,生成拓?fù)鋱D徽缚,前面也有提到憨奸,build.gradle最終會被Gradle編譯為Project字節(jié)碼,所以我們在build.gradle中編寫我們的腳本凿试,實(shí)際上就是在Project內(nèi)部編寫排宰。也就是說我們在app module的build.gradle調(diào)用編寫的方法的腳本似芝,會在解析的時(shí)候被調(diào)用,所以即使是我們跑Task clean板甘,也會輸出我們想要的結(jié)果党瓮。
4.獲取父project
//在 app build.gradle中加入以下
this.getParentProject()
def getParentProject() {
def name = "parent為null"
def parent = this.getParent()
if (parent != null) { //根Project,已經(jīng)是最頂層,沒有parent
name = parent.name
}
println "project ${this.name} 的parent為:$name"
}
//在終端執(zhí)行g(shù)radlew clean盐类,以下是輸出結(jié)果
'''
> Configure project :app
project app 的parent為:ecommerce
'''
- Gradle以樹的形式管理Project寞奸,通過前面的幾個(gè)API,已經(jīng)將所有的Project都關(guān)聯(lián)起來了,我們可以做到在任意的build.gradle(project)中獲取其它的project傲醉。另外在Gradle中根project是用來統(tǒng)籌管理所有子project的蝇闭,所以Gradle提供了更加方便的API去管理其子project
- project方法配置project
// Project project(String path, Closure configureClosure);
//在根project中的build.gradle
project('app') { Project project ->
//doSomething for app project
//比如為app 工程強(qiáng)制使用某個(gè)版本的依賴來解決依賴沖突中出現(xiàn)的依賴
project.configurations.all {
resolutionStrategy {
force 'com.android.support:support-annotations:26.1.0'
}
}
//指定輸出
apply plugin:'com.android.application'
//添加group
group 'com.github'
//指定版本
version '1.0.0'
//凡是project中可以配置的都可以進(jìn)行配置
//比如添加依賴
dependencies{
}
//添加android相關(guān)配置
android{
}
}
//同樣的還可以為其它的project配置,這里就不再配置了硬毕,和上面是一樣的呻引,將‘a(chǎn)pp’替換成其它module的名字,然后及對該module進(jìn)行配置
當(dāng)然每個(gè)Project自己所特有的吐咳,最好還是在它自己的build.gradle中配置逻悠,Gradle為每個(gè)module都提供了自己的build.gradle(project),正是為了讓它們職責(zé)分明,保證單一原則韭脊,你在你那配置你自己的東西就好了童谒。而在根project管理子project,一般都是給這些子project配置一些公共的東西沪羔,但是project方法饥伊,還是需要一個(gè)一個(gè)的配置,比如我們每個(gè)module的group蔫饰、version等都是相同的琅豆,或者說都依賴某個(gè)公共的module,每個(gè)build.gradle都需要重復(fù)配置篓吁,肯定是不符合編碼規(guī)則的茫因,就像我們編碼時(shí),重復(fù)的代碼杖剪,我們都會想辦法將它們提取出來冻押。Gradle也提供了這樣的方法。
統(tǒng)一配置
//在根project中的build.gradle
//allprojects 為當(dāng)前project及其所有子project配置
allprojects {
//倉庫
repositories {
google()
jcenter()
}
//添加group
group 'com.github'
//指定版本
version '1.0.0'
}
//打印從未配置過的compiler module的group看看是否配置成功
println project('compiler').group
//以下是輸出結(jié)果盛嘿,因?yàn)楣P記添加圖片太麻煩了洛巢,所以直接拷貝終端的輸出結(jié)果
'''
C:\Users\***\Desktop\project\ecommerce>gradlew clean
Starting a Gradle Daemon, 2 incompatible Daemons could not be reused, use --status for details
> Configure project :
com.github
BUILD SUCCESSFUL in 16s
6 actionable tasks: 4 executed, 2 up-to-date
'''
- 統(tǒng)一配置,不包括當(dāng)前project
//比如我們上傳我們的library module到Maven倉庫次兆,我們的root project一般是不需要上傳上去的狼渊,需要將子module上傳上去,就可以寫一個(gè)上傳的gradle文件,然后為所有的子module引入狈邑。
//省略了project參數(shù)
subprojects {
//庫工程才需要上傳到Maven
if (project.plugins.hasPlugin("com.android.library")){
//當(dāng)一個(gè)功能比較獨(dú)立時(shí)城须,可以寫成一個(gè)單獨(dú)的.gradle文件,然后再需要的地方apply from:'gradle文件path'米苹,即可使用該功能
apply from:'../repositories_upload.gradle'
}
}
- 以下是上傳到Maven的repositories_upload.gradle文件糕伐。為了便于理解,下面的是上傳一個(gè)library蘸嘶,因?yàn)槔锩孢€涉及到Task良瞧、屬性,引入外部文件等方面的內(nèi)容训唱,并且一些Maven的用戶名密碼褥蚯、倉庫url等都是定義在其它的地方,所以可能看起來有點(diǎn)混亂况增,但是講完之后再來看就可以看的明白了赞庶。結(jié)合注釋,絕大部分都是能夠理解的澳骤。我們學(xué)習(xí)Gradle本身就是需要使用它滿足我們開發(fā)中各種需求歧强,所以實(shí)際的應(yīng)用能讓我們更加的了解如何使用Gradle構(gòu)建項(xiàng)目并完成各種需求。
apply plugin: 'maven' //maven倉庫上傳插件
apply plugin: 'maven-publish' //maven倉庫上傳插件
configurations {
deployerJars
}
repositories {
mavenCentral()
}
// 判斷版本是Release or Snapshots
def isReleaseBuild() {
return !VERSION.contains("SNAPSHOT")
}
// 獲取倉庫url
def getRepositoryUrl() {
// return isReleaseBuild() ? RELEASE_URL : SNAPSHOT_URL
return Repository_Url
}
//配置上傳信息为肮,最重要的是這一段
//我們平時(shí)添加依賴的時(shí)候就是這樣compile 'com.android.support:support-annotations:26.1.0'
//com.android.support 對應(yīng) GROUP_ID
//support-annotations 對應(yīng) ARTIFACT_ID
//26.1.0 對應(yīng) VERSION
//而getRepositoryUrl()獲取的是你的Maven倉庫URL,userName和password分別對應(yīng)Maven倉庫的用戶名和密碼摊册,上傳到Maven倉庫之前需要有你自己Maven倉庫,這一步大家google一下基本都會了颊艳,當(dāng)然也可以發(fā)布到本地倉庫
uploadArchives {
repositories {
mavenDeployer {
pom.version = VERSION
pom.groupId = GROUP_ID
pom.artifactId = ARTIFACT_ID
pom.packaging ='aar'
repository(url: getRepositoryUrl()) {
authentication(userName: Authentication_UserName, password: Authentication_Password)
}
}
}
}
// type顯示指定任務(wù)類型或任務(wù), 這里指定要執(zhí)行Javadoc這個(gè)task,這個(gè)task在gradle中已經(jīng)定義
task androidJavadocs(type: Javadoc) {
// 設(shè)置源碼所在的位置
source = android.sourceSets.main.java.sourceFiles
}
// 生成javadoc.jar
task androidJavadocsJar(type: Jar) {
// 指定文檔名稱
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
// 生成sources.jar
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
// 產(chǎn)生相關(guān)配置文件的任務(wù)
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
題外話:學(xué)習(xí)更多優(yōu)秀的框架中的是如何使用Gradle的能幫助我們更好的學(xué)習(xí)茅特。因?yàn)槟切┦钦嬲趯?shí)際應(yīng)用中使用過的東西。并且經(jīng)過重重檢驗(yàn)棋枕。比如熱修復(fù)框架Tinker的build.gradle∥轮危現(xiàn)在看起來可能還不太能看的懂,但是也能看懂很多的東西了戒悠,只是可能里面一些屬性的配置不太了解,結(jié)合Tinker接入指南舟山,因?yàn)槭菍W(xué)習(xí)Gradle绸狐,所以看看里面的一些屬性大概是什么意思即可,也不需要深入了解累盗。建議學(xué)完之后再去看看寒矿,畢竟學(xué)習(xí)Gradle,如果能夠看懂并且學(xué)習(xí)微信在Tinker中是如何使用Gradle的若债,那么對于我們Gradle的學(xué)習(xí)肯定是很有幫助的.
屬性相關(guān)API
Gradle本身為Project提供一些屬性符相,屬性相關(guān)API可以讓我們使用這些屬性,并且讓我們可以為Project添加額外的屬性
- Project自帶屬性
/**
* The default project build file name.
* 所以所有的Project都需要一個(gè)默認(rèn)的build.gradle文件,Gradle默認(rèn)從該文件讀取配置信息
*/
String DEFAULT_BUILD_FILE = "build.gradle";
/**
* The hierarchy separator for project and task path names.
*路徑分割符啊终,如windows文件路徑使用斜杠分割
*/
String PATH_SEPARATOR = ":";
/**
* The default build directory name.
* 默認(rèn)的build輸出文件夾镜豹,默認(rèn)build產(chǎn)生的apk等產(chǎn)物在此目錄
*/
String DEFAULT_BUILD_DIR_NAME = "build";
/**
* Project-wide Gradle settings.
* IDE (e.g. Android Studio) users:
* Gradle settings configured through the IDE *will override*
* any settings specified in this file.
* 在此屬性文件中可修改一些Gradle默認(rèn)的屬性,也可擴(kuò)展屬性
*/
String GRADLE_PROPERTIES = "gradle.properties";
/**
* 以下三個(gè)屬性基本上不會使用到
*/
String SYSTEM_PROP_PREFIX = "systemProp";
String DEFAULT_VERSION = "unspecified";
String DEFAULT_STATUS = "release";
- 為Gradle擴(kuò)展屬性
Gradle自帶的默認(rèn)屬性肯定是無法滿足開發(fā)需求的,我們可以為Gradle擴(kuò)展各種各樣的屬性來管理整個(gè)項(xiàng)目
//先來看build.gradle中常見的一部分配置蓝牲,此處只做演示刪掉大部分部分配置
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
}
dependencies {
implementation 'com.android.support:appcompat-v7:26.1.0'
}
//前面說過build.gradle在配置階段會被解析成Project字節(jié)碼趟脂,所以在這里的配置實(shí)際上是在Project類中編寫代碼,而不僅僅是配置例衍。
//寫代碼的話昔期,里面的一些寫死的魔法值如編譯sdk版本26,還有寫死的字符串a(chǎn)ppcompat-v7依賴com.android.support:appcompat-v7:26.1.0都是不符合編碼風(fēng)格的
//尤其是許多依賴的版本號都是一樣的佛玄,如各種support包的版本我們可能使用一樣的硼一,寫死我們修改就需要修改多個(gè)地方,或者說多個(gè)module中都需要使用的屬性梦抢,在每個(gè)module中寫死更是如此
//所以我們可以像平時(shí)開發(fā)一樣般贼,定義一些屬性,并在配置的時(shí)候使用定義好的屬性
//直接定義屬性
apply plugin: 'com.android.application'
def mCompileSdkVersion = 26
def libSupportV7 = 'com.android.support:appcompat-v7:26.1.0'
android {
compileSdkVersion mCompileSdkVersion
}
dependencies {
implementation libSupportV7
}
//使用ext閉包擴(kuò)展屬性
apply plugin: 'com.android.application'
ext{
compileSdkVersion = 26
libSupportV7 = 'com.android.support:appcompat-v7:26.1.0'
}
android {
compileSdkVersion this.compileSdkVersion
}
dependencies {
implementation this.libSupportV7
}
//直接定義屬性和使用ext閉包擴(kuò)展屬性沒有本質(zhì)的區(qū)別
//但當(dāng)定義的屬性是所有的module都需要使用的時(shí)候,使用ext的優(yōu)勢就可以提現(xiàn)出來了,我們可以在根工程中使用ext擴(kuò)展這些屬性膳汪,然后在子工程中利用前面提到的rootProject來使用這些屬性
//定義在根工程
ext{
compileSdkVersion = 26
libSupportV7 = 'com.android.support:appcompat-v7:26.1.0'
}
//在子工程中使用
apply plugin: 'com.android.application'
android {
compileSdkVersion this.rootProject.compileSdkVersion
}
dependencies {
implementation this.rootProject.libSupportV7
}
//注意此處也可以省略掉rootProject宙刘,直接像上面使用this調(diào)用,因?yàn)樵贕radle中裆熙,根工程定義的屬性會被子工程繼承
//一個(gè)項(xiàng)目可能有很多module,需要有很多的依賴和其它配置,所有的屬性都寫在根工程中可能導(dǎo)致根工程的build.gradle文件內(nèi)容異常的多,不容易查找
//可以像上傳library到Maven一樣萤厅,將這些屬性都定義到一個(gè)單獨(dú)的文件中,然后在根工程中引入即可靴迫。
//根目錄中新建commom_properties.gradle文件(定義兩個(gè)map)
ext{
android = [compileSdkVersion:26,
buildToolsVersion:'26.0.2']
dependencies = [libSupportV7:'com.android.support:appcompat-v7:26.1.0' ,
libConstraintLayout:'com.android.support.constraint:constraint-layout:1.1.3']
}
//在根工程中引入
apply from:this.file('commom_properties.gradle') //file方法從當(dāng)前project目錄中查找文件
//在子工程中使用(ext中定義了各種map惕味,直接使用key訪問值)
apply plugin: 'com.android.application'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
}
dependencies {
implementation rootProject.ext.dependencies.libSupportV7
implementation rootProject.ext.dependencies.libConstraintLayout
}
//在gradle.properties文件中修改屬性
//Android Studio 3.0會在debug apk的manifest文件application標(biāo)簽里自動添加
//android:testOnly="true"屬性,導(dǎo)致IDE中run跑出的apk在大部分手機(jī)上只能用
//adb install -t <apk>來安裝玉锌,在oppo手機(jī)上甚至安裝不了
//解決辦法:在gradle.properties文件中修改該屬性
android.injected.testOnly=false //修改testOnly屬性為false
//添加屬性名挥,gradle.properties除了可以修改已有屬性之外,還可添加屬性主守,全局可用
//在gradle.properties文件中添加
mApplicationId = wen.github.ecommerce
mVersionCode = 1
mCompileSdkVersion = 26
//在工程中使用
apply plugin: 'com.android.application'
android {
compileSdkVersion mCompileSdkVersion
defaultConfig{
applicationId mApplicationId
versionCode mVersionCode.toInteger()
}
}
- 對于gradle屬性文件gradle.properties禀倔,想要了解更多點(diǎn)擊構(gòu)建環(huán)境
- 另外一個(gè)local.properties,用于構(gòu)建系統(tǒng)配置本地環(huán)境屬性参淫,例如SDK安裝路徑救湖。由于該文件的內(nèi)容由AS自動生成并且專用于本地開發(fā)者環(huán)境,因此不應(yīng)手動修改該文件涎才,或?qū)⑵浼{入版本控制系統(tǒng)鞋既。
-
File相關(guān)API
用于當(dāng)前Project下一些文件的處理
- 路徑獲取相關(guān)API
//獲取根工程的路徑
println getRootDir().absolutePath
//獲取build目錄路徑
println getBuildDir().absolutePath
//獲取當(dāng)前工程路徑
println getProjectDir().absolutePath
- 文件操作相關(guān)API
//查找文件
//在根工程中
println getFileContent('gradle.properties') //輸出gradle.properties的內(nèi)容
def getFileContent(String path) {
try {
def file = file(path)
return file.text
} catch (GradleException e) { //file()方法在當(dāng)前project目錄下找,找不到會報(bào)FileNotFound異常
println "File $path not found"
}
return null
}
//files()方法,接收多個(gè)路徑參數(shù)邑闺,基于當(dāng)前project工程查找多個(gè)文件跌前,返回一個(gè)collection,使用與file()方法一致
//文件的拷貝,講解Groovy語法之文件操作中通過讀取文件中的內(nèi)容检吆,然后寫入到目標(biāo)文件實(shí)現(xiàn)
//Gradle提供了更加簡便的方法:copy()
//app module的build.gradle文件中
copy {
from file('build/outputs/') //可以是文件也可以是目錄
into getRootProject().getBuildDir().path + '/rootOutputs/' //目標(biāo)路徑
include '**/*.apk' //選擇要復(fù)制的文件
include '**/*.json' //選擇要復(fù)制的文件
exclude { detail -> detail.file.name.contains('json') } //排除
rename { 'aaa.apk'} //重命名
}
//將生成的outputs文件目錄整個(gè)拷貝到根工程的build目錄下的rootOutputs文件夾中
- 源路徑文件
- 目標(biāo)路徑文件
//文件樹的遍歷
fileTree("src/main/java") {//基于基準(zhǔn)目錄創(chuàng)建文件樹
FileTree fileTree ->
fileTree.visit {//訪問文件樹的元素
FileTreeElement element ->
println element.file.name
}
}
//輸出結(jié)果
'''
wen
github
ecommerce
ECApplication.java
MainActivity.java
'''
關(guān)于文件操作的的更多細(xì)節(jié)操作可以參考官網(wǎng)的 User Guide中文件操作部分舒萎,極客學(xué)院的Gradle 用戶指南官方文檔中文版。
依賴相關(guān)API
project添加依賴蹭沛,引入外部的文件等等
//Android項(xiàng)目的根工程的build.gradle中的buildscript塊相信大家都見過臂寝,新建的項(xiàng)目的時(shí)候會自動為我們配置一些東西
//根工程的build.gradle中
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
//為什么這樣寫不知道大家有沒有考慮過,里面又能配置些什么
//buildscript塊實(shí)際上就是Project.buildscript()方法相信大家都可以理解摊灭,點(diǎn)進(jìn)方法的源碼可以看到這個(gè)閉包接收一個(gè)ScriptHandler類型的參數(shù)咆贬,那么能夠配置的東西就是ScriptHandler類里面提供的給我們的,主要有兩個(gè)配置方法repositories和dependencies帚呼。
//這個(gè)buildscript塊寫完整實(shí)際上是下面這樣掏缎,至于為什么可以去掉scriptHandler是因?yàn)殚]包中的delegate就是去掉的scriptHandler,在閉包中只要this,owner,delegate中有的方法都是可以調(diào)用的煤杀,區(qū)別是調(diào)用的順序根據(jù)閉包委托策略決定(可以到Groovy語法中講解閉包的this,owner,delegate部分了解)眷蜈。
buildscript{ ScriptHandler scriptHandler ->
//配置工程的倉庫地址
scriptHandler.repositories {}
//配置工程的“插件”依賴地址
scriptHandler.dependencies {}
}
//同樣的點(diǎn)進(jìn)repositories和dependencies方法的源碼可查看這兩個(gè)方法接收的參數(shù)以及可配置的內(nèi)容
buildscript { ScriptHandler scriptHandler ->
//配置工程的倉庫地址
scriptHandler.repositories { RepositoryHandler repositoryHandler ->
//一個(gè)項(xiàng)目可以有好幾個(gè)庫. Gradle 會根據(jù)依賴定義的順序在各個(gè)庫里尋找它們。在第一個(gè)庫里找到了就不會再在第二個(gè)庫里找它了
//所以下面都是可選的沈自,我們項(xiàng)目添加的那些依賴庫存放在哪個(gè)倉庫就需要在這配置酌儒,Gradle才能幫我們找到這些依賴庫并下載下來
repositoryHandler.google() //google倉庫
repositoryHandler.jcenter() //jcenter庫
repositoryHandler.mavenCentral() //maven中心庫
repositoryHandler.mavenLocal() //本地maven庫
repositoryHandler.maven { url "https://maven.google.com" } //配置maven私有倉庫
repositoryHandler.maven { url "https://jitpack.io" } //可以寫多個(gè)(可理解為多次調(diào)用該方法)
repositoryHandler.maven {
name '公司名稱'
url "公司私有的maven倉庫地址"
credentials { //配置倉庫用戶名和密碼
username = 'myName'
password = 'myPassword'
}
}
repositoryHandler.ivy {} //ivy倉庫
flatDir { dirs '../${項(xiàng)目名稱}/libs' } // aar等引入 ,這里只是聲明了存放aar的路徑枯途,還需要在使用到aar的module的build.gradle的dependencies中引入對應(yīng)的aar( implementation(name: 'aar名字', ext: 'aar') )
//注意如果是library類型的module中引入了aar,除了在該library module中引入忌怎,依賴了這個(gè)library的module中也需要再次引入
}
//配置工程的“插件”依賴地址,需要注意在這是scriptHandler的dependencies方法,而Project也有一個(gè)dependencies方法(即app module中的dependencies塊)酪夷。
//兩者區(qū)別:我們的應(yīng)用程序的開發(fā)都會引入各種第三方的依賴庫榴啸,這些依賴庫在Project的dependencies方法中配置,
//而Gradle實(shí)際上也是一個(gè)編程框架晚岭,那么在開發(fā)Gradle工程時(shí)也需要一些第三方的插件鸥印,這些插件就在scriptHandler的dependencies方法中配置
scriptHandler.dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' //引入這個(gè)gradle之后就可以將我們的工程指定為android類型module或者是library類型module
classpath 'com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1' //騰訊熱修復(fù)插件,引入熱修復(fù)補(bǔ)丁包生成插件工具
}
}
//引用一個(gè)外部依賴需要使用 group, name 和 version 屬性. 如下
dependencies {
compile group: 'com.android.support', name: 'appcompat-v7', version: '26.1.0'
}
//有一種簡寫形式坦报,只使用一串字符串 "group:name:version".如下,一般都是使用這種方式進(jìn)行配置
dependencies {
compile 'com.android.support:appcompat-v7:26.1.0'
}
//app工程的build.gradle中
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') //添加文件樹依賴(本地的jar包)库说,另外還可以添加file和files依賴,取決于添加的依賴是文件樹還是單一文件或者多個(gè)文件
implementation 'com.android.support.constraint:constraint-layout:1.1.3' //依賴遠(yuǎn)程jar包
testImplementation 'junit:junit:4.12' //單元測試
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //android中的測試
implementation project(':ec') //依賴源碼庫工程
implementation project(':core') //依賴源碼庫工程
annotationProcessor project(':compiler') //編譯期注解的依賴
//optional, help to generate the final application
provided('com.tencent.tinker:tinker-android-anno:1.9.1') //為了在編譯的時(shí)候生成一個(gè)Application類燎竖,使用時(shí)不需要
//tinker's main Android lib
compile('com.tencent.tinker:tinker-android-lib:1.9.1')
debugCompile 'com.amitshekhar.android:debug-db:1.0.0' //數(shù)據(jù)庫debug
}
Android Studio 2.x 版本和3.x依賴指令的區(qū)別
2.x版本
-
compile
參與編譯和打包,使用compile方式依賴的第三方庫中所有的類要销,資源文件等都會被打包到項(xiàng)目的最終產(chǎn)物中(apk,jar,aar等) -
provided
編譯占位构回,只參與編譯的過程,不會打包到apk,適用于只在編譯的時(shí)候起作用在運(yùn)行期不需要使用的庫,比如Tinker的tinker-android-anno纤掸,只為了在編譯的時(shí)候生成需要的Application類脐供,運(yùn)行時(shí)就不再需要這個(gè)工具了,另外適用于當(dāng)前工程需要引入一個(gè)主工程已經(jīng)引入的庫時(shí)借跪,比如在library module中作為工具類政己,先用provided占位,編譯通過掏愁,在使用的時(shí)候由用戶根據(jù)需求歇由,如果要使用該功能的話,再引入一次果港。 -
apk
只在生成apk的時(shí)候參與打包沦泌,編譯時(shí)不會參與 -
testCompile
參與單元測試代碼的編譯以及最終打包測試apk -
debugCompile
參與debug模式的編譯和最終的debug apk打包 -
releaseCompile
參與release模式的編譯和最終的release apk打包
- 3.x版本
-
implementation
對于使用了該命令編譯的依賴,對該項(xiàng)目有依賴的項(xiàng)目將無法訪問到使用該命令編譯的依賴中的任何程序辛掠,即將該依賴隱藏在內(nèi)部谢谦,而不對外部公開(非傳遞依賴)
簡單來說,從Android Studio 3.X開始萝衩,依賴首先應(yīng)該設(shè)置為implement回挽,如果沒有錯(cuò)那就用implement,如果有錯(cuò)猩谊,那么使用api指令千劈,這樣會使編譯速度有所增快。假設(shè)有A预柒,B,C三個(gè)module,B,C依賴于A队塘,那么在A中使用implementation依賴的庫,在B,C中無法直接使用宜鸯,在A中使
用implementation依賴的庫更改時(shí)憔古,只在A中重新編譯,在B,C中不需要編譯所以會加快編譯速度 -
api
同2.x版本的compile,參與編譯和打包淋袖,使用compile方式依賴的第三方庫中所有的類鸿市,資源文件等都會被打包到項(xiàng)目的最終產(chǎn)物中(apk,jar,aar等) -
compileOnly
同2.x版本的provided,編譯占位,只參與編譯的過程即碗,不會打包到apk,適用于只在編譯的時(shí)候起作用在運(yùn)行期不需要使用的庫焰情,另外適用于當(dāng)前工程需要引入一個(gè)主工程已經(jīng)引入的庫時(shí) -
runtimeOnly
同2.x版本的apk,只在生成apk的時(shí)候參與打包,編譯時(shí)不會參與 -
testImplementation
同2.x版本的testCompile,參與單元測試代碼的編譯以及最終打包測試apk -
debugImplementation
同2.x版本的debugCompile,參與debug模式的編譯和最終的debug apk打包 -
releaseImplementation
同2.x版本的releaseCompile,參與release模式的編譯和最終的release apk打包 -
annotationProcessor
編譯注解依賴剥懒,只參與編譯過程内舟,不會打包到apk,目的是在編譯時(shí)動態(tài)生成代碼。
-
compileOnly(provided) 和 annotationProcessor的區(qū)別
都是只參與編譯期初橘,不會打包進(jìn)apk產(chǎn)物验游,而annotationProcessor只是為了在編譯期生成代碼充岛,compileOnly(provided)是在引入一個(gè)已經(jīng)被其它工程(主工程)引入的庫,那么使用該依賴指令讓該類庫在編譯期使用耕蝉,保證編譯通過崔梗,而在運(yùn)行期使用時(shí)使用的是被其它工程引入的庫,保證只引入該類庫一次垒在。
-
依賴沖突和傳遞依賴的解決
當(dāng)我們不小心重復(fù)引入了第三方庫時(shí)蒜魄,而這兩個(gè)第三方庫的版本不同的時(shí)候,就會導(dǎo)致依賴沖突
我們依賴的第三方庫又依賴了其它第三方庫场躯,而它依賴的第三方庫是我們不需要使用的庫谈为,這個(gè)時(shí)候就需要解除傳遞依賴
//版本依賴沖突解決
//1. 強(qiáng)制依賴指定版本
configurations.all {
resolutionStrategy.force 'com.android.support:support-annotations:26.1.0'
}
//完整寫法,排除multidex
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support') {
if (!requested.name.startsWith("multidex")) {
details.useVersion '26.1.0'
}
}
}
}
//2. 排除
implementation ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
exclude group: 'io.reactivex' //排除指定包下的所有庫
}
implementation 'io.reactivex.rxjava2:rxjava:2.0.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
//另外如果沖突的是其中的一個(gè)module
implementation ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
exclude module: '沖突的module name' //排除指定module
}
//傳遞依賴
//假設(shè)有工程A,B,C,工程A依賴了B推盛,而B又依賴了C峦阁,這個(gè)時(shí)候就構(gòu)成了傳遞依賴
//而在開發(fā)中一般是不允許A直接使用C的,因?yàn)榭赡蹷升級了之后耘成,不需要再依賴C了榔昔,如果在A中直接使用了工程C中的東西的話,就會導(dǎo)致各種編譯錯(cuò)誤瘪菌,因?yàn)锽已經(jīng)沒有依賴C了撒会,在A中使用的C中的東西都已經(jīng)找不到了
conpile ('com.squareup.retrofit2:adapter-rxjava:2.1.0'){
transitive false //禁止傳遞依賴
}
//在Gradle中默認(rèn)transitive就是false,另外在Android Studio 2.x 版本和3.x依賴指令的區(qū)別部分师妙,我們也可以知道诵肛,使用implementation,本身也是非傳遞依賴,在B中使用implementation依賴C默穴,A再依賴B怔檩,本身C中的API那些就無法在A中直接使用
- 查看沖突的依賴庫
- 打印出該模塊所有的依賴樹信息:gradlew -q <模塊名>:dependencies
- 將項(xiàng)目切換至Project面板,在External Libraries目錄下查看
- 項(xiàng)目的Gradle Projects面板中運(yùn)行 Tasks->android->androidDependencies
- 外部命令的執(zhí)行相關(guān)API
// 使用exec()方法執(zhí)行外部命令蓄诽,對于其它的javaexec方法薛训,只要配置好環(huán)境變量,實(shí)際上通過exec方法也是可以執(zhí)行的
task(name:'apkcopy'){ //創(chuàng)建一個(gè)名為apkcopy的task
doLast{ //在task的最后執(zhí)行
def sourcePath = this.buildDir.path+'/outputs/apk'
def destinationPath = '/Users/wen/apks' //要存放的文件路徑
def command = "mv -f $sourcePath $destinationPath" //命令行
exec{
try {
executable 'bash' //要使用的可執(zhí)行文件的名稱
args '-c',command //要執(zhí)行的命令的參數(shù)仑氛。默認(rèn)為空列表
println 'the command is execute success'
} catch (GradleException e) {
println 'the command is execute failed'
}
}
}
}
執(zhí)行外部命令更多細(xì)節(jié)請點(diǎn)這里
Task相關(guān)API
為Project提供新增Task和使用已有Task的能力乙埃。了解更多點(diǎn)擊 Gradle Task
- 什么是Task
一個(gè)Task表示構(gòu)建的單個(gè)原子動作。構(gòu)建工作就是由各種Task構(gòu)成的锯岖。我們也可以定義各種各樣的Task介袜。在Gradle寫的其它腳本都會在配置階段被執(zhí)行(利用生命周期的監(jiān)聽除外),并且執(zhí)行的順序不確定出吹,而利用Task可以在執(zhí)行階段執(zhí)行我們需要的邏輯遇伞,可并為Task指定執(zhí)行順序。除此之外我們還可以為我們自己編寫的Task指定類型來讓Task具有更強(qiáng)的功能捶牢。
- Task定義和配置
//定義
//1. 通過task方法創(chuàng)建
task getDateTask {
println new Date().dateString //在終端輸入gradlew getDateTask 執(zhí)行Task輸出:18-10-16
}
//2.通過TaskContainer創(chuàng)建
this.tasks.create(name: 'getDateTask2') {
println "getDateTask2 date: ${new Date().dateString}"
}
//TaskContainer是用來管理一個(gè)Project中所有的Task的鸠珠,提供創(chuàng)建和查找task功能加派。查找對應(yīng)task最好是在配置階段之后,此時(shí)所有的task都配置好了跳芳。否則可能出現(xiàn)找不到對應(yīng)的task
//配置
//1. 創(chuàng)建的時(shí)候直接配置
task getDateTask(group:'learn',description:'task study') {
println new Date().dateString
}
task clean(type: Delete) { //配置類型
delete rootProject.buildDir
}
//2. 方法配置
task getDateTask2 {
setGroup('learn') //配置組
setDescription('task study') //配置描述
println new Date().dateString
}
-
配置組之后同一組的Task會被放到同一目錄下(右側(cè)gradle面板中可以查看),description相當(dāng)于注釋竹勉,解釋該task的作用
- Task所有可配置信息
String TASK_NAME = "name"; //名字
String TASK_DESCRIPTION = "description"; //描述
String TASK_GROUP = "group"; //所屬組
String TASK_TYPE = "type"; //類型
String TASK_DEPENDS_ON = "dependsOn"; //task依賴(依賴于其它task)
String TASK_OVERWRITE = "overwrite"; //是否重寫其它task 默認(rèn)false
String TASK_ACTION = "action"; //配置task相關(guān)邏輯
- doFirst和doLast方法
Task可以執(zhí)行在執(zhí)行階段飞盆,注意前面只執(zhí)行g(shù)etDateTask2,getDateTask也輸出了次乓,因?yàn)樵谂渲秒A段就會解析所有的Task用于構(gòu)建task拓?fù)鋱D吓歇,所以這些代碼會在配置階段就被執(zhí)行。下面利用doFirst和doLast方法讓腳本代碼執(zhí)行在執(zhí)行階段
task getDateTask(group: 'learn', description: 'task study') {
println new Date().dateString
//在閉包中調(diào)用
doFirst { println "the group is:$group" }
}
//在閉包外調(diào)用
getDateTask.doFirst { println "the description is:$description" }
可以看到票腰,task中沒有被doFirst包裹的代碼依舊在配置階段就被執(zhí)行了城看,而doFirst中的代碼是在執(zhí)行app中的getDateTask時(shí)執(zhí)行。并且在閉包外調(diào)用的doFirst先執(zhí)行
至于doFirst和doLast的執(zhí)行區(qū)別看下面執(zhí)行結(jié)果相信大家就能明白了
task getDateTask(group: 'learn', description: 'task study') {
println new Date().dateString
doLast {println "the description2 is:$description"}
//在閉包中調(diào)用
doFirst { println "the group1 is:$group" }
doFirst { println "the group2 is:$group" }
}
//在閉包外調(diào)用
getDateTask.doFirst { println "the description1 is:$description" }
- doFirst和doLast最常用的地方是讓我們的腳本代碼執(zhí)行在已有Task的執(zhí)行之前或執(zhí)行之后
//統(tǒng)計(jì)項(xiàng)目構(gòu)建時(shí)間
def startBuildTime, endBuildTime
this.afterEvaluate { Project project ->
//在配置階段之后(所有的task都被配置好杏慰,構(gòu)建了task拓?fù)鋱D)查找task 防止找不到對應(yīng)task
def preBuildTask = project.tasks.getByName('preBuild') //構(gòu)建最先執(zhí)行preBuild task
def buildTask = project.tasks.getByName('build') //構(gòu)建最終執(zhí)行build task
preBuildTask.doFirst {
startBuildTime = System.currentTimeMillis()
println "build start,current time is $startBuildTime"
}
buildTask.doLast {
endBuildTime = System.currentTimeMillis()
println "build end,current time is $endBuildTime"
println "The build took ${(endBuildTime - startBuildTime) / 1000} seconds"
}
}
-
Task執(zhí)行順序
在Gradle的配置階段是構(gòu)建所有Task的拓?fù)鋱D测柠,實(shí)際上可以理解為是在確定Task的執(zhí)行順序。那么Gradle是中各個(gè)Task的執(zhí)行順序是如何確定的缘滥。
-
dependsOn配置依賴決定執(zhí)行順序
在前面Task所有可配置內(nèi)容中有一個(gè)dependsOn屬性轰胁,這個(gè)屬性是用于配置Task依賴的,執(zhí)行一個(gè)task的時(shí)候朝扼,這個(gè)task所依賴的task會先被執(zhí)行赃阀,但是執(zhí)行它依賴的task對它是不會造成影響的
task taskA {
doLast {
println 'taskA run'
}
}
task taskB {
doLast {
println 'taskB run'
}
}
task taskC(dependsOn:[taskA,taskB]) { //為taskC配置依賴
doLast {
println 'taskC run'
}
}
-
可以看到單獨(dú)執(zhí)行沒有依賴的taskB只會執(zhí)行taskB,而執(zhí)行依賴了taskA、taskB的taskC,執(zhí)行之前首先會先執(zhí)行它依賴的taskA擎颖、taskB,而taskA和taskB的執(zhí)行順序?qū)嶋H上是隨機(jī)的
- 當(dāng)事先不太清楚具體要依賴哪些Task,可以動態(tài)指定
//動態(tài)的指定依賴
task taskA(group:'gradle'){
doLast {
println 'taskA run'
}
}
task taskB(group:'gradle'){
doLast {
println 'taskB run'
}
}
task taskC(group:'java'){
doLast {
println 'taskC run'
}
}
task taskD { //動態(tài)的指定task依賴
dependsOn this.tasks.findAll {task -> task.group == 'gradle' }
doLast {
println 'taskD run'
}
}
-
mustRunAfter()榛斯、shouldRunAfter() API指定執(zhí)行順序
需要注意的是,使用mustRunAfter和shouldRunAfter是在兩個(gè)task都會執(zhí)行的情境下會按照該指定順序執(zhí)行搂捧,調(diào)用了該Api指定依賴執(zhí)行順序的兩個(gè)task并沒有依賴關(guān)系驮俗。單獨(dú)執(zhí)行其中一個(gè)對另外的任務(wù)并不會有影響,但如果mustRunAfter和task依賴之間發(fā)生了沖突异旧,那么執(zhí)行時(shí)將會報(bào)錯(cuò)
task taskA{
doLast{
println 'taskA run'
}
}
task taskB{
doLast{
println 'taskB run'
}
}
task taskC{
doLast{
println 'taskC run'
}
}
taskB.mustRunAfter(taskC)
taskA.mustRunAfter(taskB)
- API指定task的排序意述,并不會指定它們之間的依賴關(guān)系,所以只執(zhí)行其中一個(gè)task吮蛹,'必須在它之前執(zhí)行的'task不會先被執(zhí)行荤崇,而執(zhí)行task,它依賴的task才會先被執(zhí)行,下面只執(zhí)行taskA,taskB和taskC都不會執(zhí)行
- 常利用mustRunAfter()和dependsOn將我們的task插入到生命周期的task之間去完成特定的需求
//假設(shè)Gradle構(gòu)建的生命周期中有taskA和taskB,taskA執(zhí)行之后會執(zhí)行taskB,潮针,那么需要將myTask术荤,插入taskA和taskB中間
//以下是偽代碼
project.afterEvaluate{
//1.找到taskA和taskB
//可以通過TaskContainer的findTaskByName方法或其它方式獲取到響應(yīng)的task
//假設(shè)已找到taskA和taskB
//2.將myTask插入到taskA之后
myTask.mustRunAfter taskA
//3. 將myTask插入到taskB之前
taskB.dependsOn myTask
}
-
finalizedBy
如果需要實(shí)現(xiàn)執(zhí)行某一個(gè)task之后必須執(zhí)行另一個(gè)task,用上面的方法是沒辦法的每篷,此時(shí)可以利用finalizedBy API
task taskA{
doLast{
println 'taskA run'
}
}
task taskB{
doLast{
println 'taskB run'
}
}
//執(zhí)行完taskA之后必須執(zhí)行taskB
taskA.finalizedBy taskB
TaskInputs瓣戚、TaskOutputs
輸入輸出在Gradle中用于增量構(gòu)建端圈,執(zhí)行一個(gè)任務(wù),如果在編譯時(shí)子库,gradle判斷在從上一次編譯中舱权,該task的輸入輸出沒有任何改變,那么gradle就會跳過該task仑嗅,前提是這個(gè)task至少有一個(gè)輸入或輸出Task源碼中獲取輸入輸出的方法
/**
* <p>Returns the inputs of this task.</p>
*
* @return The inputs. Never returns null.
*/
@Internal
TaskInputs getInputs();
/**
* <p>Returns the outputs of this task.</p>
*
* @return The outputs. Never returns null.
*/
@Internal
TaskOutputs getOutputs();
- TaskInputs支持的輸入類型
/**
* 返回所有task的輸入文件
*
* @return 返回輸入文件宴倍,如果task沒有輸入文件返回一個(gè)空的文件集合
*/
FileCollection getFiles();
/**
* 指定該task的輸入文件(多個(gè)文件)
*
* @param 輸入文件路徑
* @return 返回property builder以進(jìn)一步配置屬性
*/
TaskInputFilePropertyBuilder files(Object... paths);
/**
*指定該task的輸入文件(一個(gè)文件)
*
* @param 輸入文件路徑.
* @return 返回property builder以進(jìn)一步配置屬性
*/
TaskInputFilePropertyBuilder file(Object path);
/**
* 為該task注冊一個(gè)輸入目錄。在給定目錄下找到的所有文件都被視為輸入文件
*
* @return 返回property builder以進(jìn)一步配置屬性
*/
TaskInputFilePropertyBuilder dir(Object dirPath);
/**
* 返回該task的輸入屬性集仓技。
*
* @return The properties.
*/
Map<String, Object> getProperties();
/**
* 為該task注冊一個(gè)輸入屬性鸵贬。該屬性值在task執(zhí)行時(shí)持久化,并task的后
* 調(diào)用的屬性值進(jìn)行比較脖捻,以確定task是否最新阔逼。
*
*屬性的給定值必須是可序列化的,并且應(yīng)該提供一個(gè)有效的equal()方法
*
*也可指定一個(gè)閉包作為屬性的值,在這種情況下會執(zhí)行該閉包用于確定屬性的值
*
* @param name The name of the property. Must not be null.
* @param value The value for the property. Can be null.
*/
TaskInputs property(String name, Object value);
/**
* 為該Task注冊一個(gè)輸入屬性集合
*
* @param properties The properties.
*/
TaskInputs properties(Map<String, ?> properties);
//注意TaskInputFilePropertyBuilder繼承于TaskInputs地沮,用于描述包含零個(gè)或多個(gè)文件的task的輸入屬性嗜浮。
- TaskOutputs支持的輸出類型
/**
* 返回該Task的所有輸出文件
*
* @return The output files. Returns an empty collection if this task has no output files.
*/
FileCollection getFiles();
/**
* 指定該Task的輸出是文件(多個(gè))
*
*如果是一個(gè)map,key必須是有效的java標(biāo)識符摩疑,否則task輸出緩存會被禁用
*
* @param paths The output files.
*
* @see CacheableTask
*/
TaskOutputFilePropertyBuilder files(Object... paths);
/**
* 該Task輸出是文件目錄
*
*
* @param paths The output files.
*
* @see CacheableTask
*
*/
TaskOutputFilePropertyBuilder dirs(Object... paths);
/**
* 指定該Task的輸出是文件(單個(gè))
*
*/
TaskOutputFilePropertyBuilder file(Object path);
/**
* task輸出文件是dir
*/
TaskOutputFilePropertyBuilder dir(Object path);
//注意TaskOutputFilePropertyBuilder繼承于TaskOutputs,用于描述包含零個(gè)或多個(gè)文件的task的輸出屬性周伦。
- 總結(jié):TaskInputs支持單個(gè)文件、多個(gè)文件和文件目錄以及以key-value形式的單一屬性和key-value形式的屬性map,需要注意這些key-value都需要是serializable的未荒,因?yàn)樾枰涗浧饋碜ㄅ玻籘askOutputs支持單個(gè)文件、多個(gè)文件和文件目錄
-
SourceSet修改工程中各項(xiàng)資源文件的位置
當(dāng)我們新建一個(gè)Android項(xiàng)目的時(shí)候片排,AS幫我們創(chuàng)建一系列的目錄和文件寨腔,比如放置代碼的java目錄,放置資源的res目錄率寡,main目錄下AndroidManifest.xml文件等迫卢,那么為什么編寫的代碼文件就需要放置在java目錄,資源就要放入res目錄呢?是因?yàn)榫幾g項(xiàng)目時(shí)冶共,Gradle默認(rèn)會從這些相應(yīng)的目錄中加載相應(yīng)的代碼和資源乾蛤,這是約定好的,如果我們沒有做任何改動捅僵,就會按照它默認(rèn)的方式去加載代碼和資源家卖。所以我們可以根據(jù)我們的需要修改這些默認(rèn)的選項(xiàng),借助SourceSet提供的方法庙楚,我們可以按照我們想要的方式來配置代碼和資源的加載目錄
//在AS中上荡,gradle默認(rèn)去加載jniLibs目錄中的 *.so 文件
sourceSets { //soutceSet方法重定位資源文件的位置
main {//jniLibs在main目錄下,所以需要調(diào)用main方法
jniLibs.srcDirs = ['libs'] //修改到libs目錄中加載*.so文件
}
}
//修改res的目錄馒闷,有時(shí)候?yàn)榱吮阌诠芾砝壹瘢瑔为?dú)的一個(gè)模塊的資源文件都放在一個(gè)單獨(dú)的目錄中叁征,想要gradle能夠正確識別到這些文件,需要而外添加進(jìn)來
sourceSets {
main {
//指定從多個(gè)路徑加載資源文件
res.srcDirs = [
'src/main/res/chat-res',
'src/main/res'
]
}
}
- 對于更多的SourceSet的內(nèi)容可以到Gradle官網(wǎng)SourceSet部分查閱逛薇,對于Android開發(fā)者來說可以查閱Android Gradle DSL SourceSet部分或者直接點(diǎn)進(jìn)源碼查看一下AndroidSourceSet提供了配置哪些資源文件的位置的方法
-
構(gòu)建變體
對于Android開發(fā)者來說捺疼,項(xiàng)目最終打包成APK,了解這個(gè)APK的各個(gè)屬性的配置尤為重要永罚。