Gradle 構建Android Studio 項目詳解

Gradle入門

每一個基于gradle構建的項目处坪,都應該至少有一個build.gradle 文件
Android的構建文件中, 有一些元素是必須的 比如

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
    }
}

這就是實際構建配置的地方 盼樟。在repositories 代碼快中 jcenter庫被配置為整個構建過程的依賴倉庫
jcenter 是一個預配置的Maven倉庫 不需要額外的配置

每一個Android項目都應該申請該插件

apply plugin: 'com.android.application'

如果你正在構建一個依賴庫 那么需要申請library插件

apply plugin: 'com.android.library'

當你使用Android插件時 ,不僅可以配置針對Android的特殊約定 還可以生成只應用于Android的任務 锈至。
下面的Android代碼片段是由插件來定義的恤批,可以配置在每個項目中:

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"

}

這是配置Android特殊約定參數的一部分 Android插件為Android的開發(fā)需求提供了一整套DSL 。
唯一需要的參數屬性是編譯目標和構建工具

基本自定義構建

理解Gradle 文件

當用Androidstudio創(chuàng)建一個新項目時 裹赴, 會默認生成三個gradle文件
其中兩個文件 settings.gradle 和 build.gradle 位于項目的根目錄
另外一個build.gradle 文件則在 app模塊內被創(chuàng)建

settings.gradle文件

對于一個只包含一個Android應用的新項目來說 settings.gradles文件應該是這樣的
include ':app'
settings.gradle文件在初始化階段被執(zhí)行 并且定義了哪些模塊應該包含在構建內

頂層構建文件

在項目中 所有模塊的配置參數都應該在頂層build.gradle文件中配置 默認情況下其包含如下兩個代碼塊

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

repositories 代碼塊配置倉庫地址
dependencies 代碼塊用于配置構建過程中的依賴包
allprojects 代碼塊可用來聲明那些需要被用于所有模塊的屬性

模塊的構建文件

模塊層的build.gradle 文件的屬性只能應用在Android app模塊喜庞, 它可以覆蓋頂層build.gradle文件的任何屬性
該模塊的構建文件示例如下

apply plugin: 'com.android.application'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"
    defaultConfig {
        applicationId "com.bhb.seniorcustomview"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.google.android.material:material:1.3.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

從下到下依次為三個模塊 插件 Android 依賴包

1 插件 第一行用到了Android應用插件 該插件在頂層構建文件中被配置成了依賴
2 Android 在構建文件中 占比最大的是android代碼塊 。 該代碼塊包含了全部的Android特有配置棋返,
這些配置之所以可被使用延都,是因為之前我們使用的Android插件
必須有的屬性是
compileSdkVersion 用來編譯應用的Android API版本
buildToolsVersion 構建工具和編譯器使用的版本號
構建工具包含命令應用 如aapt、zipalign睛竣、dx和renderscript 晰房, 這些都被用來打包在打包應用時生成各種中間產物

def aultConfig代碼塊用于配置應用的核心屬性
applicationId 應用唯一id
minSdkVersion 應用運行的最小API級別
targetSdkVersion 應用運行目標API級別
versionCode 1 應用版本號
versionName "1.0" 應用版本名

buildTypes 代碼塊用來定義如何打包和構建不同類型的應用 在后面會詳細介紹

  1. 依賴包 依賴代碼塊是標準Gradle配置的一部分,其定義了一個應用或依賴項目的所有依賴包

任務入門

要想知道一個項目中有哪些任務可被使用, 則可以運行gradlew tasks 任務殊者,該命令會打印出所有可用的任務

基礎任務

Gradle的Android插件使用了Java基礎插件 与境, 而Java基礎插件又使用了基礎插件
基礎插件定義了 assemnle和clean 任務
java基礎插件定義了 check和buildtasks任務
它們被用來定義插件之間的約定,約定如下:
assemble 集合項目的輸出
clean 清理項目的輸出
check 運行所有檢查
build 同時運行assemble和check

Android任務

Android插件擴展了基本任務
assemble 為每個構建版本創(chuàng)建一個APK
clean 刪除所有的構建內容 猖吴,例如APK
check 運行Lint檢查 如果發(fā)現問題即可終止構建
build 同時運行assemble和check

assemble任務默認依賴于 assembleDebug 和 assembleRelease 如果你添加了更多的構建類型摔刁,那么
就會有更多的任務 。 這意味著海蔽,運行assemble將會觸發(fā)每一個構件類型共屈,并進行一次構建操作

除了這些之外 Android插件還添加了一些新的任務
connectedCheck 在連接設備或模擬器上運行測試
deviceCheck 一個占位任務 , 專為其他插件在遠端設備上運行測試
installDebug和installRelease 在連接的設備上安裝特定版本
所有install任務都會有相關的 uninstall任務

BuildConfig 和資源

構建工具會生成一個叫做BuildConfig 的類党窜,
你可以通過gradle來擴展該文件 這樣在 debug和release時, 就可以擁有不同的常量
這些常量可用于切換服務器url或其他屬性例如

buildTypes {

        debug {
            buildConfigField "String", "API_URL", "\"https://test.com.api\""
        }

        release {buildConfigField "String", "API_URL", "\"https://com.api\""
        }
    }

字符串必須用轉義雙引號括起來 這樣才會生成實際意義上的字符串
在添加buildConfig之后 可以在代碼中使用 BuildConfig.API_URL

項目范圍的設置

如果在一個項目中 你有多個Android模塊 并且每個模塊都需要設置相同的編譯版本
我們可以在頂層文件中定義build.gradle文件都能定故意的額外屬性 然后將它們應用到模塊中
添加額外屬性需要通過ext代碼塊 例如

ext{
compileSdkVersion = 30
buildToolsVersion "30.0.3"
}

然后在app 的模塊中使用
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
}

項目屬性

三種常用定義屬性的方法
1 參考上面的例子 ext代碼塊
2 gradle.proterties 文件 例如: debugmode=false

  1. -p 命令行參數
    自定義任務例子
task printProterties << {
    if(project.hasProperty('cmd')){
        println cmd
    }else {
        println "not contains propterties"
    }
}

然后在命令行運行該命令
./gradlew printProterties -Pcmd="hello from comman line"

依賴管理

依賴倉庫

依賴倉庫可以認為是文件的集合 可以通過repositories代碼塊添加倉庫地址
除了上面提到過的jcenter 也可以添加自己創(chuàng)建的maven倉庫

allprojects {
    repositories {
        google()
        jcenter()


        //個人maven倉庫
        maven {
            url "https://mymaven.com"
        credentials{
            username 'user'
            password 'password'
        }
        }

        //本地路徑倉庫
        maven {
            url "../file"
        }

        //本地aar路徑倉庫
        flatDir {
            dirs 'aars'
        }

        maven { url "https://jitpack.io" }

    }
}

然后在 dependencies 中添加具體需要依賴的項目
有兩種寫法
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation group:'androidx.appcompat' , name: 'appcompat' , version: '1.0.2'

文件依賴

// 添加本地依賴文件
implementation fileTree(dir: 'libs')
// 添加本地依賴文件 添加過濾 只有.jar文件會被依賴
implementation fileTree(dir: 'libs', include: ['*.jar'])

添加原生依賴庫 so文件

Android插件支持原生依賴庫 需要做的就是在模塊層創(chuàng)建一個jniLibs文件夾 拗引,
然后為每個架構創(chuàng)建子文件夾 , 將so文件放在適當的文件夾中

如果此約定不生效 幌衣, 那么你可以在構建文件中設置相關位置
android {

sourceSets.main{
    jniLibs.srcDir 'src/main/libs'
}

}

項目依賴

implementation project(':library')

依賴關鍵字

compile                        該配置不僅會將依賴添加至類路徑還會生成對應的APK
implementation            替代compile 類似于private引用的依賴只有本模塊可以使用
api                                替代compile 類似于public引用的依賴本模塊和引用本模塊的項目都可以使用
apk                               該配置不會將依賴添加至類路徑 只會生成對應的APK
provided                       該配置不會生成對應的APK 只會將依賴添加至類路徑
testImplementation       只在test時有效
androidTestImplementation 只在androidTest時有效

語義化版本

版本化是依賴管理的重要部分 將依賴添加到JCenter等依賴庫時 約定遵循了一套版本化規(guī)則 我們稱之為語義化版本
在語義化版本中矾削,版本數字的格式一般為 major.minor.patch 數字按照規(guī)則依次增加
1.當做不兼容的API變化時 major版本增加
2.當以向后兼容的方式添加功能時, minor版本增加
3.當修復一些bug時豁护, patch版本增加

動態(tài)化版本

在某些情況下 你可能希望在每次構建應用時 都能獲取到最新的依賴
想要實現這一點 最好的實現方式是使用動態(tài)化版本 例如

// 表示我們想要 1.0.x 最新的版本
implementation 'androidx.appcompat:appcompat:1.0.+'

// 表示我們想要獲取最新的 1.x版本 x至少是0
implementation 'androidx.appcompat:appcompat:1.0+'

//表示我們想要獲取最新的版本
implementation 'androidx.appcompat:appcompat:+'

創(chuàng)建構建Variant

前面提到過 每個由Androidstudio 創(chuàng)建的新項目都會生成debug和release構建類型
另外一個概念是product flavor 構建類型和product flavor的結合結果被稱之為構建variant

構建類型

下面是AS創(chuàng)建的標準buildTypes代碼塊

android {
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

創(chuàng)建構建類型

當默認的設置不夠用時 我們可以很容易的創(chuàng)建自定義構建類型
新的構建類型只需要在buildtypes代碼塊中新增一個對象即可 例如 下面staging自定義構建類型

 debug {

            minifyEnabled false     // 打開代碼壓縮
            shrinkResources false   // 打開資源壓縮
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            applicationIdSuffix ".debug"    //修改debug applicationid后綴
            versionNameSuffix '-debug'    // 修改debug 版本號后綴
        }

因為我們設置了applicationIdSuffix debug 和release 的applicationId不一致 所以可以同時在一臺機器上安裝兩個版本

第二種寫法

 staging.initWith(buildTypes.release)
        staging{
            applicationIdSuffix ".staging"
        }

賦值一個已有的類型 然后覆蓋其中需要修改的屬性

資源集合

配置buildtypes 之后 可以在對應的資源目錄下配置 屬于不同類型的 資源文件
例如 配置的buildtypes 有 debug release staging
那么目錄結構如下
app---src--||
---debug ---||
---res --- strings.xml
---main
---java ---- package.com --- MainAvtivity.java
---release
---res --- strings.xml
--- staging
---res --- strings.xml

java 類型只能有一份 但資源文件 xml drawable等文件可以有多份 打不同的構建類型下讀取不同的文件

創(chuàng)建 product flavor

創(chuàng)建product flavor 和構建類型類似 可以通過在productFlavor 代碼塊中添加新的Product flavor來創(chuàng)建

android {

    productFlavors{
        red{
            applicationIdSuffix 'com.gradleandroid.red'
            versionCode 3
        }
        blue{
            applicationIdSuffix 'com.gradleandroid.blue'
            versionCode 4
            minSdkVersion  14
        }
    }

product flavor 也可以和 buildtypes一樣定義資源集合 方式和buildtypes一樣
當然也可以和buildtypes組合起來定義資源集合 該文件夾的名稱是 flavor名稱+構建類型名稱
例如 blue + release 的資源集合文件夾名稱是 blueRelease

多種定制版本

在某些情況下 你可能想要更進一步 創(chuàng)建product flavor的結合體
例如 客戶A 和 客戶B 中都想要免費版和付費版 并且是基于相同的代碼 不同的品牌
創(chuàng)建四種不同的flavor 意味著需要像這樣設置多個拷貝 所以這不是最佳的做法
使用flavor 維度是結合flavor的有效方式 如下所示

android {

    flavorDimensions "color","price"

    productFlavors{
        red{
            flavorDimensions "color"
            applicationIdSuffix 'com.gradleandroid.red'
            versionCode 3
        }
        blue{
            flavorDimensions "color"
            applicationIdSuffix 'com.gradleandroid.blue'
            versionCode 4
            minSdkVersion  14
        }
        free{
            flavorDimensions "price"
        }
        paid{
            flavorDimensions "price"
        }
    }
}

當結合了兩個flavor時 他們可能定義了相同的屬性
這種情況下 flavor維度數組的順序 靠前的會覆蓋掉靠后的
上一個例子中 color維度覆蓋了price維度 該順序也決定了構建variant的名稱
假設構建類型有 debug 和release 那么上面例子定義的flavor將構成下面這些 variant

blueFreeDebug 和 blueFreeRelease
bluePaidDebug 和 bluePaidRelease
redFreeDebug 和 redFreeRelease
redPaidDebug 和 redPaidRelease

任務

Gradle 的Android插件會為配置的每一個variant創(chuàng)建任務
一個新的Android應用默認有 debug 和release兩種構建類型
所以可以用 assembleDebug 和 assembleRelease 來分別構建兩個apk 即用單命令assemble構建兩個apk
當添加一個新的構建類型時 新的任務也將被創(chuàng)建 一旦你開始添加flavor 那么整個全新的任務系統(tǒng)將會被創(chuàng)建
因為每個構建類型的任務都會和每個product flavor相結合
這意味著 僅用一個構建類型和一個flavor 做一個簡單設置 你就會有三個任務用于構建全部的variant
assembleBlue 使用blue flavor配置和組裝BlueRelease 及 BlueDebug
assembleDebug 使用debug類型 給每一個flavor組裝一個debug版本
assembleBlueDebubg 用構建類型結合flavor配置 并且flavor將會覆蓋構建類型的設置

新的tasks視為每個構建類型 每個product flavor 結合而創(chuàng)建的

variant 過濾器

android.variantFilter {
    variant ->
        if(variant.buildType.name.equals('release')){
            variant.getflavors().each() {
                flavor -> {
                    if(flavor.name.equals('blue')){
                        variant.setIgnore(true)
                    }
                }
            }
        }
}

簽名配置

android {

    // 簽名模塊
    signingConfigs {
        release {
                keyAlias 'release_android'
                keyPassword 123456
                storeFile file('./release.jks')
                storePassword 123123
          
        }

 debug {
                keyAlias 'debug_android'
                keyPassword 123456
                storeFile file('./debug.jks')
                storePassword 123123
        }
    }
}

在android 代碼塊下 設置debug 和 release 版本的簽名信息
在buildtypes模塊中使用簽名信息

  buildTypes {
        release {
            signingConfig signingConfigs.realse
    }
}

在flavor 中使用簽名信息

android {

    flavorDimensions "color","price"

    productFlavors{
        red{
            singingConfig singingConfigs.release
            flavorDimensions "color"
            applicationIdSuffix 'com.gradleandroid.red'
            versionCode 3
        }
    }
}

創(chuàng)建任務和插件

groovy 語法不熟悉的同學 可以先去翻看別的文章

定義任務

任務屬于project對象 并且每個任務都可以執(zhí)行task接口 哼凯。
定義一個新任務的最簡單的方式是 執(zhí)行將任務名稱作為其參數的任務方法
task hello
然后定義任務方法體

task hello {

    println 'hello '    //1
    doFirst {            //2
        println 'hello world first'
    }
    doLast {          //3
        println 'hello world last'
    }
}

運行任務 命令行 ./gradlew hello

task任務.png

可以看見 步驟1在 任務執(zhí)行前就已經被打印 而 步驟2 3 是在任務執(zhí)行后才被打印
在Gradle 構建中 都有三個階段 初始化階段-配置階段-執(zhí)行階段
步驟1 為配置階段 23為執(zhí)行階段
配置階段 即便執(zhí)行的不是當前任務 也會被執(zhí)行 在這里我們添加一個hello2任務 然后執(zhí)行

task hello {

    println 'hello '
    doFirst {
        println 'hello world first'
    }
    doLast {
        println 'hello world last'
    }
}

task hello2 {

    println 'hello2 '
    doFirst {
        println 'hello2 world first'
    }
    doLast {
        println 'hello2 world last'
    }
}
task任務2.png

dolast方法 和 dofirst方法可以定義多個

task first{
    doFirst {
        println 'not really first'
    }

    doFirst {
        println 'first'
    }

    doLast {
        println 'not really last'
    }

    doLast {
        println 'last'
    }
}

運行之后如圖


task任務3.png

需要注意的是 dofirst 總是添加一個動作到task 的最前面 而 dolast 總是添加一個動作到最后面
要注意定義的順序

當涉及到給task任務排序時 可以使用 mustRunAfter() 方法
hello.mustRunAfter hello2
命令行運行
./gradlew hello hello2
結果如圖

mustRunAfter.png

如果你需要一個任務依賴于另一個 , 那么可以使用dependsOn 方法
hello.dependsOn hello2
結果如圖


dependsOn.png

總結
mustRunAfter 任務可以獨立執(zhí)行
dependsOn 任務不會獨立執(zhí)行 并且依賴項目執(zhí)行在前

自動重命名apk

  android.applicationVariants.all {
        variant ->
            variant.outputs.all {
                               outputFileName = "${variant.versionName}-${variant.versionCode}.apk"
            }
    }

這里可以根據variant 和屬性 自行判斷是什么分支 獲取該分支的versionname 和versioncode 對outputFileName進行封裝

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末择镇,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子括改,更是在濱河造成了極大的恐慌腻豌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘱能,死亡現場離奇詭異吝梅,居然都是意外死亡,警方通過查閱死者的電腦和手機惹骂,發(fā)現死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門苏携,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人对粪,你說我怎么就攤上這事右冻。” “怎么了著拭?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵纱扭,是天一觀的道長。 經常有香客問我儡遮,道長乳蛾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮肃叶,結果婚禮上蹂随,老公的妹妹穿的比我還像新娘。我一直安慰自己因惭,他們只是感情好岳锁,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筛欢,像睡著了一般浸锨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上版姑,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天柱搜,我揣著相機與錄音,去河邊找鬼剥险。 笑死聪蘸,一個胖子當著我的面吹牛,可吹牛的內容都是我干的表制。 我是一名探鬼主播健爬,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼么介!你這毒婦竟也來了娜遵?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤壤短,失蹤者是張志新(化名)和其女友劉穎设拟,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體久脯,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡纳胧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了帘撰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跑慕。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖摧找,靈堂內的尸體忽然破棺而出核行,到底是詐尸還是另有隱情,我是刑警寧澤蹬耘,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布钮科,位于F島的核電站,受9級特大地震影響婆赠,放射性物質發(fā)生泄漏绵脯。R本人自食惡果不足惜佳励,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛆挫。 院中可真熱鬧赃承,春花似錦、人聲如沸悴侵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽可免。三九已至抓于,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浇借,已是汗流浹背捉撮。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妇垢,地道東北人巾遭。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像闯估,于是被迫代替她去往敵國和親灼舍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容