Gradle基礎
Gradle的生命周期分為三個不同的階段:初始化 -》 配置 -》 構建
初始化:settings.gradle
配置:build.gradle
構建:gradle
初始化階段會讀取根目錄下setting.gradle的include信息,決定哪些工程會加入構建過程,并且創建project實例。
配置階段會按引用樹去執行所有工程的build.gradle腳本,配置project對象,一個對象由多個任務組成。
運行階段會根據Gradle命令傳遞過來的Task名稱,執行相關依賴任務。
版本參數優化
每個module的build.gradle文件都擁有一些必要的屬性,同一個Android工程中,在不同的模塊中要求這些屬性一致,如果不一致會造成資源 的重復,另一方面會降低編譯效率。那么就必須有一個統一的、基礎的gradle配置。
資源引用配置
1 使用sourceSets的方式來指定文件的路徑。
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
2 可以動態添加res資源,在buildtype 和productFlavor中定義resValue變量
resValue "string","app_name","xxx"
resValue只能動態添加資源,無法替換資源。如果資源名重復,Gradle會提示重復定義資源。
3可以指定特定尺寸的資源
resConfigs "hdpi","xhdpi","xxhdpi"
4 通過build.gradle編譯生成BuildConfig文件,可以直接讓代碼讀取到BuildConfig中的值。
buildConfigField("String","BASE_URL","\"https://xxx.com.cn\"")
在buildConfig文件中就會得到:
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 10101;
public static final String VERSION_NAME = "1.01.01";
// Fields from build type: debug
public static final String BASE_URL = "https://xxx.com.cn";
}
Gradle加載的優先級
BuildType -> productFlavor -> Main -> dependencies
最左邊的優先級越高
Build types
Gradle在Android中的build type是用來處理app或者library應該被構建成什么類型,在這個配置中,我們可以定義應用的包名是什么,是否自動去除掉沒有引用的資源,是否開啟混淆等等。
buildTypes {
release {
//設置正式包名稱
applicationVariants.all { variant ->
variant.outputs.all { output ->
project.ext { appName = 'xxx' }
def newName = project.ext.appName +"-" + defaultConfig.versionName + "-" +buildTime() + ".apk"
outputFileName = new File(newName)
}
}
//開啟混淆
minifyEnabled true
//Zipalign優化
zipAlignEnabled true
//移除無用的resource文件
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
applicationVariants.all { variant ->
variant.outputs.all { output ->
project.ext { appName = 'XXX' }
def newName = project.ext.appName +"-" + defaultConfig.versionName + "-" +buildTime() + ".apk"
outputFileName = new File(newName)
}
}
}
Product flavors
與build type相反,build type用來構建不同類型的app,debug版或release版,而produc flavors則是用來在同一個app上創建不同的版本,如付費版和免費版。一個非常常見的應用場景就是我們創建了一個銀行管理的app給不同銀行提供服務,但是不同的銀行App的logo不一樣,有produc flavors就能在基于一套代碼上創建不同版本的app或者library。
productFlavors {
red {
applicationId 'com.gradleforandroid.red'
versionCode 3
}
blue {
applicationId 'com.gradleforandroid.blue'
minSdkVersion 14
versionCode 4
}
}
Multiflavor variants
在某些情況下,我們可能需要進行flavors的組合,比方說你的app有兩套主題,綠色主題和紅色主題,然后有兩個版本,付費版和免費版,你可能需要進行組合,類似于紅色免費版,紅色付費版,綠色免費版,綠色付費版。通過使用flavorDimensions可以解決flavors組合的問題,
flavorDimensions "color", "price"
productFlavors {
red {
flavorDimension "color"
}
blue {
flavorDimension "color"
}
free {
flavorDimension "price"
}
paid {
flavorDimension "price"
}
}
Variant filters
在某些情況下,可能我們不想使用某種build variant,例如現在有debug和release的build type,red和blue的flavors,但是,blue還是測試環境根本現在用不到blue的release版本,那么我們可以直接過濾掉,在android studio中的buildVariants窗口就不會出現了,過濾可以在app模塊或者library模塊的build.gradle文件中加入如下代碼:
android.variantFilter { variant ->
if (variant.buildType.name.equals('release')) {
variant.getFlavors().each() { flavor ->
if (flavor.name.equals('blue')) {
variant.setIgnore(true);
}
}
}
}
gradle的依賴特性
1 implementation
會將指定的依賴添加到編譯路徑,并且會將該依賴打包到輸出,如apk中,但是這個依賴在編譯時不能暴露給其他模塊,例如依賴此模塊的其他模塊。這種方式指定的依賴在編譯時只能在當前模塊中訪問。
例如:
有兩個模塊app 和test,模塊app依賴test,test添加了遠程二進制庫依賴joda-time,在test模塊中使用沒有問題,在app模塊中使用則報錯。
2 api
使用api配置的依賴會將對應的依賴添加到編譯路徑,并將依賴打包輸出,但是這個依賴是可以傳遞的,比如模塊A依賴模塊B,B依賴庫C,模塊B在編譯時能夠訪問到庫C,但是與implemetation不同的是,在模塊A中庫C也是可以訪問的。
3 compileOnly
compileOnly修飾的依賴會添加到編譯路徑中,但是不會打包到apk中,因此只能在編譯時訪問,且compileOnly修飾的依賴不會傳遞
4 runtimeOnly
這個與compileOnly相反,它修飾的依賴不會添加到編譯路徑中,但是被打包到apk中,運行時使用。
5 annotationProcessor
用于注解處理器的依賴配置,像第三方庫 ButterKnife
Gradle的一些配置
開啟Gradle的守護進程來構建項目:
org.gradle.daemon=true
如果你要構建一個多Module并且依賴關系比較復雜的項目,那么你可以使用并行項目執行:
org.gradle.parallel=true
在Gradle主目錄中配置的屬性優先級高于在項目中配置的屬性。當你并不想一個個項目的去改動配置時,可以定義一份常用的Gradle配置文件放在Gradle的主目錄下
對于內存較小的機器避免Gradle編譯卡頓
修改gradle.properties,避免影響其它人編譯速度,把修改的gradle.properties文件放到用戶文件夾.gradle下
#設置最大堆內存為1.5g和最大非堆內存0.5g
org.gradle.jvmargs=-Xmx1536m -XX\:MaxPermSize\=512m -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8
#不啟用守護進程
org.gradle.daemon=false
#不啟用并行編譯
org.gradle.parallel=false
#使用構建緩存
android.enableBuildCache=true
修改build.gradle文件,如下:
dexOptions {
//最大堆內存
javaMaxHeapSize "2g" // 1g should be also OK
}
壓縮Apk大小
加快Build速度
Gradle的Android插件在 buildType 上有一個名為 minifyEnabled 的布爾屬性,我們需要將其設置為true以啟用ProGuard:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
壓縮Resource文件
自動壓縮的方式很簡單,就是在構建中配置 shrinkResources 屬性。如果將此屬性設置為true,Android構建工具將自動檢測哪些資源未被使用,并且不會將它們包括在APK中。
android {
buildTypes {
release {
minifyEnabled = true
shrinkResources = true
}
}
}
使用 resConfigs 屬性配置要保留的資源,然后其余的將被拋出。
defaultConfig {
resConfigs "en", "da", "nl"
resConfigs "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
}
忽略Lint檢查
當使用Gradle執行構建時,Android構建任務將會對代碼執行Lint檢查。Lint是一個靜態代碼分析工具,用于標記布局和Java代碼中的潛在錯誤。在某些情況下,Lint一旦報錯,構建就會停止。如果之前沒有在項目中使用Lint,并且我們想遷移到Gradle,Lint可能會出現很多錯誤。為了使構建工作不會因為Lint檢查而中斷,你可以配置Gradle來忽略Lint錯誤,并通過禁用 abortOnError 來阻止它們中止構建。這只是一個臨時解決方案,因為忽略Lint檢查可能會產生像丟失翻譯類似這樣的潛在錯誤,這可能會導致應用程序崩潰的問題。為了防止Lint阻塞構建過程,可以像下面這樣禁用abortOnError:
android {
lintOptions {
abortOnError false
}
}