關於Gradle
什麼是Gradle
Gradle是基於Apache Ant和Apache Maven概念的自動化構建工具。他使用一種基於Groovy的DSL來宣告專案設定。
Gradle能夠:
自動處理包相依關係(取自 Maven Repos 的概念)
自動處理佈署問題(取自 Ant 的概念)
條件判斷寫法直覺(使用 Groovy 語言)
Gradle的編譯過程
Gradle中有兩個物件:Project、Task。
每個專案的編譯至少有一個Project,一個build.gradle就代表一個project,每個project裡面包含了多個task,task裡包含了多格action。action包含了需要被執行的程式碼。
編譯過程中,Gradle會根據build相關檔案,聚合所有的project和task,執行task中的action。
執行順序由依賴邏輯來保證。所有的task都需要依賴其他task來執行,沒有被依賴的task首先被執行。
編譯過程分為:
1.初始化階段: 建立project物件。
2.配置階段: 執行所有的編譯指令碼,同事建立project的所有task。
3.gradle根據傳入的引數決定如何執行這些task。
Gradle Files
setting.gradle 用於定義加入編譯過程的module
頂層的build.gradle 最終被應用到所有專案中
頂層的build.gradle
buildscript:定義了Android編譯工具的類路徑。
allprojects:定義的屬性會被應用到所有module中。
每個專案的build.gradle
1.apply plugin:定義了gradle外掛。
eg:
apply plugin "com.android.application"
2.android:用於配置android的所有特殊配置。這個是前年宣告的plugin提供的
(1)defaultConfig:程式的預設配置。
(2)buildType:定義了編譯型別。可以針對每個型別進行不同的配置。(release。debug等)
3.dependencies:是gradle的依賴配置。定義了當前專案需要依賴的其他庫。
Gradle Wrapper
gradle wrapper用於處理對以往專案的相容性問題。
Gradle basics
Gradle會根據build檔案的配置生成不同的task。我們可以直接單獨執行每個task。
可以使用./gradlew task
列出所有的task。如果想要列出每個task對應依賴的其他task,可以使用./gradlew task -all
Android tasks
基本的四個task:
1.assemble:對所有的buildType 生成apk包
2.clean:移除所有的編譯輸出檔案,比如apk包
3.check:執行lint檢測編譯
4.build:同時執行assemble和check命令
Configuration
BuildConfig
可以在buildTypes中配置一些key-value對。他們會體現在BuildConfig中
repositories
Repositories:就是程式碼倉庫,我們平時的新增的一些 dependency就是從這裡下載的,Gradle 支援三種類型的倉庫:Maven,Ivy和一些靜態檔案或者資料夾。
在執行階段,gradle會從倉庫中取出對應需要的依賴檔案。
除了使用遠端庫以外,還可以使用相對路徑配置本地倉庫:
repositorys { flatDir { dirs "aars" } }
Local dependencies
File dependencies
通過files()方法可以新增檔案依賴,也可以通過fileTree()新增一個資料夾:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
Native libraries
配置本地.so
庫:
android { sourceSets.main{ jniLibs.srcDir 'src/main/libs' } }
Library projects
如果要寫一個library專案讓其他專案引用,則需要使用一下plugin:
apply plugin: 'com.android.library'
Build Variants
編譯時動態根據當前的編譯型別輸出不用樣式的apk檔案。這時候就需要使用buildType了
BuildType
Source Sets
每當建立新的build type的時候,gradle預設會建立一個資訊source set。我們可以建立與main資料夾同級的資料夾,根據編譯型別的不同可以選擇對某些原始碼直接進行替換。
Product flavors
前面build type的做法能夠對同一份原始碼編譯同一個程式的不同型別,如果我們需要針對同一份原始碼編譯不同的程式,就需要用到Product flavors;
所有的product flavor版本和defaultConfig共享所有屬性
和build type一樣,product flavor也有自己的source set資料夾。除此之外,build type和product flavor可以結合,他們的資料夾裡面的檔案優先順序甚至高於單獨的build type和product flavor資料夾的優先順序。
android { flavorDimensions "color", "price" productFlavors { red { flavorDimension "color" } blue { flavorDimension "color" } free { flavorDimension "price" } paid { flavvorDimension "price" } } }
Signing configurations
簽名相關。
當我們打包市場版時,我們需要輸入keystore資料,如果是debug版本系統會幫我們配置這些資訊。這些資訊在gradle中都配置在signingConfigs中:
android { signingConfigs { staging.initWith(signingConfigs.debug) release { storeFile file("release.keystore") storePassword "secretpassword" keyAlias "gradleforandroid" keyPassword "secretpassword" } } }
配置之後可以再build type中直接使用
android { buildTypes { release { signingConfig signingConfigs.release } } }
Optimize
Speeding up multimodule builds
可以通過以下方式加快gradle編譯:
開啟並行編譯:在專案根目錄下的gradle.properties中設定
org.gradle.parallel=true
開啟編譯守護程序:該程序在第一次啟動後會一直存在,當第二次編譯的時候,可以重用該程序。
org.gradle.daemon=true
加大可用編譯記憶體:
org.gradle.jvmargs=-Xms256m -Xmx1024m
Reducing apk file
在編譯時,我們可能會有很多資源並沒有用到,此時可以通過shrinkResources
來優化我們的資原始檔,除去不必要的資源
android { buildTypes { release { minifyEnabled = true shrinkResources = true } } }
某些情況下,一些資源需要通過動態載入的方式載入,這時候就需要在res/raw/下簡歷一個keep.xml檔案,通過以下方式keep資源:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" />
Manual shrinking
對於一些特殊檔案或者資料夾,例如國際化資原始檔、螢幕適配資源等。如果我們已經確定過了某種幸好,而不需要重新適配,就可以去掉不可能被適配的資源:
android{ defaultConfig { resConfigs "hdpi", "xhdpi", "xxhdpi", "xxxhdpi" } }
Profiling
當我們執行所有task的時候,都可以新增–profile引數生成一份執行報告在reports/profile
中。
Practice
在開發中,我們可能需要自己自定義task。這就需要用到Groovy了。
Groovy
Groovy我打算專門去學習一下。這裡就不記錄了。
通過hook Android編譯外掛重新命名apk
android.applicationVariants.all { variant.outputs.each { output -> def file = output.outputFile output.outputFile = new File(file.parent, file.name.replace(".apk", "-${variant.versionName}.apk")) } }