天天看點

gradle 詳解 android gradle配置指南Gradle基礎Android Gradle配置

文章目錄

  • Gradle基礎
      • Gradle環境配置
      • Android Gradle建構生命周期
      • Gradle Wrapper
      • Groovy基礎
        • 字元和字元串
        • 集合,List集合,Map集合
        • 閉包
        • 自定義屬性
        • 腳本即代碼,代碼也是腳本
      • Gradle插件
        • 二進制插件
        • 應用腳本插件
        • 第三方插件
  • Android Gradle配置
      • 預設配置
      • signingConfigs 簽名資訊配置
      • buildTypes 建構類型
      • sourceSets
      • packagingOptions
      • 批量修改生成的apk檔案名
      • 動态生成版本資訊
      • 從git的tag中擷取版本号
        • 動态擷取版本名稱
        • 動态擷取版本号
      • 隐藏簽名檔案資訊
      • 動态配置AndroidManifest檔案
      • 自定義BuildConfig
      • 動态添加自定義的資源
      • Java編譯選項
      • DEX選項配置
      • lintOptions

Gradle基礎

Gradle環境配置

下載下傳gradle解壓後找到目錄,例如:D:\android\gradle\gradle-4.4\bin。把位址配置到環境變量path中

Android Gradle建構生命周期

  • Initialization:初始化階段,會執行項目根目錄下的settings.gradle檔案,來分析哪些項目參與建構
  • Configuration:配置階段,配置階段會去加載所有參與建構的項目的build.gradle檔案,

    會将每build.gradle檔案執行個體化為一個Gradle的project對象

  • Execution:執行階段,開始打包

Gradle Wrapper

  • Gradle的一層包裝,便于開發中統一建構版本。當啟動Gradle時,Wrapper會檢查Gradle有沒有被下載下傳關聯。 在 Wrapper中包含gradle-wrapper.jar和gradle-wrapper.properties。
  • gradlew和gradlew.bat分别是Linux和Windows下的可執行腳本。Gradle-wrapper.jar是具體業務邏輯包,gradlwe最終還是使用Java執行的這個jar。
  • gradle-wrapper.properties中的配置:distributionBase 下載下傳的gradle壓縮包解壓後存儲主路徑;distributionPath 相對于distributionBase的解壓後的gradle壓縮包的路徑; zipStoreBase 同distributionBase ,隻不過是存放zip壓縮包的; zipStorePath 同distributionPath ,隻不過是存放zip壓縮包的; distributionUrl Gradle發行版本壓縮包的下載下傳位址。

Groovy基礎

Groovy是基于JVM虛拟機的一種動态語言, 它的文法和Java非常相似,是一種靈活的動态腳本語言。每個Gradle的build腳本都是一個Groovy腳本。Groovy是弱類型語言,文法更加靈活,不用像Java中的類以及資料類型需要強制轉換,如下示例:

字元和字元串

task hello << {
      def name='a‘
      println '單引号:${name}‘
      println "雙引号:${name}"
	  }
//輸出結果
單引号:${name}
雙引号:a
           
  • 不管是單引号還是雙引号都是String類型。
  • 單引号沒有運算能力。

集合,List集合,Map集合

//List集合
task printList << {
      def numList=[1,2,3,4,5,6]
      println numList[1]//通路第二個元
      println numList[-1]//通路最後一個元素
      println numList[-2]//通路倒數第二個元素
      println numList[1..3]//通路第二個到第四個元素
}
//Map集合
task printMap << {
      def map=["width":1080,"height":800]
      println map["width"]//通路第二個元素
      map.each{
             println "Key:${it.key},Value:${it.value}"
      }
}
           

閉包

閉包在很多語言中都有這種概念,在Java8中Lambda表達式也是閉包的形式。另外在javascript,kotlin中都有閉包的概念。如下示例:

定義方法customEach,它隻有一個參數用于接收閉包代碼塊。如果隻有一個參數,就用it代替。

task printClosure << {
      //使用我們自定義的閉包
      customEach{
             println it
      }
}
 //自定義方法
def customEach(closure){
     for(int i in 1..10){
             closure(i)
     }
}
           

定義方法eachMap,為閉包傳遞兩個參數,key ,value,多個參數就不能用it了,必須要顯示聲明出來。

task printClosure << {
      //使用我們自定義的閉包
      eachMap{k , v ->
                 println "${k} is ${v}"
      }
}
//自定義方法
def eachMap(closure){
      def map=["name":"張三","age":18]
      map.each{
             closure(it.key,it.value)
      }
}
           

自定義屬性

//自定義 工具版本号
ext.build_tools_version = "25.0.2"

//引用版本号 
buildToolsVersion build_tools_version
           

腳本即代碼,代碼也是腳本

這段代碼是生成時間,可以用于打封包件名

def buildTime(){
    def date=new Date()
    def formattedDate=date.format('yyyyMMdd')
    return formattedDate
}
           

Gradle插件

二進制插件

apply plugin:'java’
           

二進制插件就是實作了org.gradle.api.Plugin接口,需要有plugin id。

應用腳本插件

apply from:'version.gradle’ 
           

應用腳本插件就是執行了version.gradle一段腳本代碼。

第三方插件

apply plugin:'com.android.application’ 

classpath ‘com.android.tools.build:gradle:1.5.0’
           

第三方釋出的作為jar的二進制插件。

Android Gradle配置

預設配置

compileSdkVersion 28
 buildToolsVersion '25.0.1'
 defaultConfig {
        applicationId "com.sort.demo"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
 }
           
  • applicationld:用于指定生成app的包名。
  • minSdkVersion:最低支援的sdk版本。
  • targetSdkVersion:用于配置基于哪個Android Sdk開發。
  • compileSdkVersion:目前app編譯版本。
  • versionCode:用于配置App内部版本号,通常用于版本更新。
  • versionName:用于配置App的版本名稱,是讓使用者知道目前的版本。
  • resConfigs:過濾語言

signingConfigs 簽名資訊配置

signingConfigs{
        debug{
            storeFile file("debug.keystore")
            storePassword "password"
            keyAlias "test"
            keyPassword "password"
        }
        release{
            storeFile file(“release.keystore”) //簽名證書檔案
            storePassword “password” //簽名證書檔案密碼
            keyAlias “test” //簽名證書秘鑰别名
            keyPassword “password” //簽名證書秘鑰密碼
        }
 }
           

signingConfigs 需要寫在defaultConfig 和buildTypes 前面,不然會編譯報錯。預設情況下debug模式簽名已經配置好了,在$HOME/.android/debug.keystore。

buildTypes 建構類型

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

buildTypes的屬性:

  • applicationIdSuffix:應用id字尾
  • versionNameSuffix:版本名稱字尾
  • minifyEnabled:是否混淆
  • proguardFiles:混淆檔案
  • singningConfig:簽名配置
  • manifestPlaceholders:清單占位符
  • shrinkResources:是否去除未使用的資源
  • zipAlignEnable:是否使用zipalign工具壓縮
  • multiDexEnabled:是否拆分成多個Dex
  • multiDexKeepFile:指定文本檔案編譯進主Dex檔案中
  • multiDexKeepProguard:指定混淆檔案編譯進主Dex檔案中

sourceSets

每一個BuildType都會生成一個SourceSet,預設位置為src//。一個SourceSet包含源代碼,資源檔案等資訊。針對不同的BuildType,可以單獨為其指定Java源代碼,res資源等。jniLibs目錄下放置的是so庫檔案,直接放在目錄下即可。如果so是在libs中,那就需要單獨配置了,如下jniLibs.srcDirs=[‘libs’]。

sourceSets {
            main {
                res.srcDirs =
                        [
                                'src/main/res/google',
                                'src/main/res/baidu'
                        ]
                jniLibs.srcDirs = ['libs']
            }
 }
           

開發中我們會引用到aar這樣的架包, 如果aar中的libs存在架包,也需要單獨配置,配置如下:

repositories {
    flatDir {
        dirs 'libs'
    }
 }
           

使用abiFilters 過濾,其實這個可以不設定,這樣編譯時,就會将項目裡所有依賴資源包裡的so庫都打到最終的apk裡。但是有些平台,我們是不需要支援的,如果不删除的話,apk就臃腫了。如果那些so庫是我們自己編譯出來的,那可以直接在工程中删除對應so檔案,但是如果是第三方提供的,就不好删除了,是以就需要使用abiFilters來過濾了。

defaultConfig {
        ndk {
            abiFilters 'armeabi','armeabi-v7a','arm64-v8a'
        }
}
           

packagingOptions

用于解決檔案沖突問題

packagingOptions {
        exclude ’META-INF/LICENSE.txt‘
        exclude ’META-INF/LICENSE‘
 }
           

用于解決項目中的jar沖突

compile ('com.wdullaer:materialdatetimepicker:3.2.2') {
    exclude group: 'com.android.support', module: 'support-v4'
    exclude group: 'com.android.support', module: 'design'
}
           

簡寫方式

compile() { dep ->
    ['support-v4', 'support-v13', 'design'].each{ module -> dep.exclude module: module }
}
           

批量修改生成的apk檔案名

applicationVariants.all { variant ->
        variant.outputs.all { output -> // AS 3.0之後版本 each 改為 all
            def fileName = "${buildTime()}_release.apk"
            def outFile = output.outputFile
            if (outFile != null && outFile.name.endsWith('.apk')) {
                outputFileName = fileName// AS 3.0之後版本 output.outputFile 改為 outputFileName
            }
        }
}
           

動态生成版本資訊

在開發中,一個工程中有多個module,每個module都有build.gradle配置,這些配置中的版本資訊可以統一管理和維護。如下示例:

建立config.gradle内容如下

ext {
    android = [
            compileSdkVersion: 22,
            minSdkVersion    : 19,
            targetSdkVersion : 22,
            buildToolsVersion: '25.0.0',
            versionCode      : 1,
            versionName      : "1.0.1",
    ]
    dependencies = [
            supportV4: 'com.android.support:support-v4:22.2.1',
            design   : 'com.android.support:design:22.2.0'
    ]
}
           

在主工程中引入config.gradle

apply from: "config.gradle"
           

module中引用配置的内容

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
    defaultConfig {
        applicationId "com.example.demo"
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode rootProject.ext.android.versionCode
        versionName rootProject.ext.android.versionName
    }
}
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile rootProject.ext.dependencies.supportV4
    compile rootProject.ext.dependencies.design
}
           

從git的tag中擷取版本号

git describe --abbrev=0 --tags //擷取目前tag名
           

動态擷取版本名稱

defaultConfig {
        versionCode appVersionCode
        versionName getAppVersionName()
 }
           

從git tag中擷取應用的版本名稱

def getAppVersionName() {
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'cmd','git', 'describe', '--abbrev=0', '--tags'
        standardOutput = stdout
    }
    return stdout.toString()
}
           

動态擷取版本号

defaultConfig {
        versionCode getAppVersionCode()
        versionName getAppVersionName()
}
           

以git tag數量作為版本号

def getAppVersionCode(){
    def stdout=new ByteArrayOutputStream()
    exec {
        commandLine 'cmd','git','tag','--list'
        standardOutput = stdout
    }
    return split("\n").size()
}
           

隐藏簽名檔案資訊

在團隊開發中如果想隐藏簽名資訊,把正式包的時候不受影響。把資訊存到主機環境變量中,既做到隐藏也能正常打包。

signingConfigs {
        def appStoreFile=System.getenv("STORE_FILE")
        def appStorePassword=System.getenv("STORE_PASSWORD")
        def appKeyAlias=System.getenv("KEY_ALIAS")
        def appKeyPassword=System.getenv("KEY_PASSWORD")
        //當不能從環境變量裡擷取到簽名資訊的時候,就使用項目中帶debug簽名
        if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
            appStoreFile="debug.keystore"
            appStorePassword="android"
            appKeyAlias="androiddebugkey"
            appKeyPassword="android"
        }
        release{
            storeFile (appStoreFile)
            storePassword appStorePassword
            keyAlias appKeyAlias
            keyPassword appKeyPassword
        }
}
           

動态配置AndroidManifest檔案

在我們做管道統計的時候一般會這樣做,在打包的時候會把${UMENG_CHANNEL}替換成管道名。

<meta-data android:value="${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>
           

根據管道名來表示管道号,這樣在管道統計的時候就能統計不同的管道。

productFlavors{
        google{
            manifestPlaceholders.put("UMENG_CHANNEL","google")
        }
        baidu{
            manifestPlaceholders.put("UMENG_CHANNEL","baidu")
        }
 }
           

簡寫方式

productFlavors {
        google { }
        baidu { }
        productFlavors.all { flavor ->
             manifestPlaceholders.put("UMENG_CHANNEL", name)
        }    
}
           

自定義BuildConfig

比如:不同的管道想實作打開不同的網頁,可以這麼做。

productFlavors {
        google {
            buildConfigField 'String','WEB_URL', ' http://www.google.com '
        }
        baidu {
            buildConfigField 'String','WEB_URL', ' http://www.baidu.com '
        }
}
           

比如:測試包用測試環境,正式包用正式環境。

buildTypes {
        debug{
            buildConfigField ' String ', ' WEB_URL ', ' http://www.ceshi.com '
        }
        release {
            buildConfigField 'String','WEB_URL', ' http://www.zhengshi.com '
        }
}
           

比如:測試包開啟日志,正式包關閉日志

buildTypes {
        debug{
            buildConfigField 'bool', 'isDebug', 'true'
        }
        release {
            buildConfigField 'bool', 'isDebug', 'false'
        }
 }
           

動态添加自定義的資源

res/values中的資源,不光可以在res/values中使用xml的方式定義,還可以在gradle中定義。這樣不同的管道引用的string是不同的。還可以使用id,bool,dimen,integer,color等類型定義value資源。在BuildType中也可使用。

productFlavors {
        google {
           resValue 'string','channel_tips','google管道歡迎你'
        }
        baidu {
           resValue 'string','channel_tips','baidu管道歡迎你'
        }
}
           

Java編譯選項

compileOptions {
        encoding=‘utf-8’//指定檔案編碼格式
        sourceCompatibility=JavaVersion.VERSION_1_8 //源代碼編譯級别
        targetCompatibility=JavaVersion.VERSION_1_8//生成Java位元組碼的版本
}
           

DEX選項配置

在apk打包的時候被dx指令優化成Android虛拟機可執行的DEX檔案。預設情況下被dx配置設定記憶體是一個G,也就是1024MB。

dexOptions {
        javaMaxHeapSize ‘4g‘ //執行dx最大堆記憶體
        jumboMode true  //忽略方法數限制的檢查,apk無法再低版本的裝置上面安裝
        threadCount 2    //dx使用的線程數
}
           

lintOptions

程式在編譯的時候會檢查lint,有任何錯誤提示會停止build,我們可以關閉這個開關。

lintOptions {
        abortOnError false //即使報錯也不會停止打包
        checkReleaseBuilds false  //打包release版本的時候進行檢測
}