介紹
現(xiàn)在我們知道了Gradle如何工作,如何創(chuàng)建自己的Task以及Plugin购撼,如何執(zhí)行test任務(wù)斋扰,如何設(shè)置CI。這一章會包含一些小技巧央星,接下來會從以下Topic進行討論:
- Reducing the APK file size
- Speeding up builds
- Ignoring Lint
- Advanced app deployment
Reducing the APK file size
APK文件的大小在最近幾年都在瘋長霞怀。有很多的原因,更多的Library等曼,更多的Densities里烦,App功能越來越強大。GooglePlay限制了APK大小50M禁谦,而一個更小的APK也就意味著用戶會更快的下載和安裝胁黑,并且減少內(nèi)存空間的占用。
在這一節(jié)我們來看看如何通過Gradle構(gòu)建配置來減少APK大小州泊。
ProGuard
ProGuard除了可以shrink(壓縮)丧蘸,也可以進行optimize(優(yōu)化),obfuscate(混淆),在編譯時期進行preverify(預驗證)力喷。它通過應(yīng)用程序中的所有代碼路徑來查找未使用的代碼并刪除它刽漂。ProGuard也會重命名你的類和屬性。這個過程會使得內(nèi)存占用更小弟孟,更難逆向贝咙。
Android Plugin在buildType
中有一個Boolean的屬性名為minifyEnabled
,可以設(shè)置成true啟用Proguard:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
當你設(shè)置了minifyEnabled
為true后拂募,proguardRelease
任務(wù)就會執(zhí)行庭猩,并且在構(gòu)建過程中調(diào)用ProGuard
。在啟用了ProGuard之后陈症,最好重新測試一下整個APP蔼水,有可能它仍然把你一些有用的代碼都移除了,比如說JNI中調(diào)用的Java代碼录肯。為了解決這個問題趴腋,你可以定義ProGuard rules來把一些真正有用的代碼保證不被移除。proguardFiles
屬性就是用來定義包含了ProGuard Rules的文件论咏。例如优炬,要Keep一個雷,你可以如下定義:
-keep public class <MyClass>
getDefaultProguardFile('proguard-android.txt')````這個函數(shù)會獲取
proguard-android.txt文件作為默認的ProGuard配置文件潘靖。而該文件就在Android SDK的
tools/proguard目錄下穿剖。而
proguard-rules.pro```文件會默認添加到新的Android Modules中蚤蔓,所以你可以在Modules中進行簡單的Rule配置卦溢。
具體的ProGuard配置,可以參照官網(wǎng)壓縮代碼和資源
Shrinking resources
Gradle和Android Plugin在App打包的時候秀又,會把沒用的資源都刪掉单寂。如果你有一個舊的資源沒有刪除,那么它就會默認幫你刪除掉吐辙。另外一個方面是當你引用了很多資源的Library宣决,但是你只是用一小部分,你可以通過啟用resource shrinking的方式來移除昏苏。這有兩種方式來壓縮資源尊沸,自動或者手動
Automatic shrinking
如果設(shè)置了shrinkResources
屬性為true的話,Android Build Tools將會自動的決定哪些資源是沒用的贤惯,并且不把它們打包到APK中洼专。
這個特性也有一個要求,就是你需要啟用proGuard
孵构。正因為Resource Shrinking工作了屁商,Android Build Tools不能指出哪些資源是無用的,直到這些代碼引用的資源全部被移除颈墅。
在BuildType中自動配置資源Shrinking:
android {
buildTypes {
release {
minifyEnabled = true
shrinkResources = true
}
}
}
如果你希望看到你的APK減少了多少蜡镶,你可以執(zhí)行shrinkReleaseResources
這個任務(wù)雾袱。這個任務(wù)會打印出來包大小減少了多少:
:app:shrinkReleaseResources
Removed unused resources: Binary resource data reduced from 433KB to 354KB: Removed 18%
你可以通過添加--info
標志位來獲取移除的資源信息:
$ gradlew clean assembleRelease --info
當使用這個Flag的時候,Gradle會打印出在構(gòu)建過程中的很多其他信息官还,包括最終沒有打入APK包中的每一個資源芹橡。
而Automatic Resource Shrinking有一個問題,就是它可能會移除大量的資源望伦。尤其是使用動態(tài)獲取的資源僻族,可能會被移除掉。為了避免這種情況屡谐,我們可以在res/raw/
目錄下創(chuàng)建一個keep.xml
文件述么,用來保持資源:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/keep_me,@layout/also_used_*"/>
而這個keep.xml文件本身也不會被打入最終的包中。
Manual shrinking
減少資源的一種不極端的方案是減少多密度愕掏,多語言等文件度秘。某些Library中包含了很多語言,例如Google Play Services饵撑。如果你的APP只想支持一個或者兩個語言剑梳,而不想把所有的語言都打入最終的APK中。你可以使用resConfigs
屬性來配置你希望保留的資源滑潘,而剩下的都會被丟棄垢乙。
如果你希望保存English,Danish语卤,Dutch的字符串追逮,你可以使用resConfigs
如下:
android {
defaultConfig {
resConfigs "en", "da", "nl"
}
}
你同樣也可以在density進行選擇:
android {
defaultConfig {
resConfigs "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
}
}
它甚至可以組合Launguages和Density。實事上粹舵,每一種資源類型都可以通過這個屬性進行配置钮孵。如果你不想配置ProGuard,或者你只想去掉不支持的語言和Density眼滤,那么使用resConfigs
是一個不錯的選擇巴席。
Speeding up builds
Gradle的構(gòu)建速度會比Ant長一些,因為Gradle在構(gòu)建的生命周期中有三個階段诅需,而當你每次執(zhí)行Task的時候漾唉,它都會經(jīng)過這三個階段。這會使得整個過程都很容易進行配置堰塌,但是確實會比較慢赵刑。不過,我們也有一些方法能夠提升Gradle構(gòu)建速度蔫仙。
Gradle properties
一種提升速度的方法就是修改默認的設(shè)置料睛。我們之前提到過parallel構(gòu)建,你可以通過設(shè)置parallel屬性來提升構(gòu)建速度。
首先在Top-Level創(chuàng)建一個gradle.properties
文件恤煞。然后添加:
org.gradle.parallel=true
另外一種方式是啟用Gradle Daemon屎勘。啟用后,會在第一次啟動構(gòu)建的時候啟動一個后臺進程居扒。當后續(xù)的構(gòu)建啟動時概漱,都會使用這個后臺進程,因此會節(jié)省一些啟動的開銷喜喂。這個進程會在你使用Gradle期間一直存在瓤摧,而在空閑3個小時后關(guān)閉。使用Daemon在短時間內(nèi)構(gòu)建是非常有用的玉吁。你可以在gradle.properties
中添加:
org.gradle.daemon=true
在Android Studio中照弥,Gradle Daemon是默認啟用的。我這也就意味著在IDE中第一次啟動構(gòu)建后进副,后續(xù)的構(gòu)建都會比較快这揣。如果你從命令行執(zhí)行構(gòu)建的話,Gradle Daemon則是關(guān)閉的影斑,除非在Properties中啟用给赞。
為了提升編譯本身的速度,你可以設(shè)置JVM的參數(shù)矫户。在Gradle的屬性中片迅,名為jvmargs
,可以用來為JVM啟用設(shè)置內(nèi)存分配的值皆辽。這兩個參數(shù)也會對構(gòu)建速度有直接的影響:Xms
和Xmx
柑蛇。
-
Xms
:用來設(shè)置初始化使用的內(nèi)存總值
-Xmx
:用來設(shè)置內(nèi)存使用最大值
可以在gradle.properties
文件中添加:
org.gradle.jvmargs=-Xms256m -Xmx1024m
默認的Xmx是256M,而Xms沒有設(shè)置膳汪。這兩個選項都是基于你電腦的能力唯蝶。
最后一個你可以配置的影響構(gòu)建速度的屬性是:org.gradle. configureondemand
。如果具有多個模塊的復雜項目遗嗽,則此屬性特別有用,因為它試圖通過跳過正在執(zhí)行任務(wù)不需要的模塊來限制配置階段花費的時間鼓蜒。如果設(shè)置了這個屬性痹换,那么Gradle就會在配置階段前,查出哪個模塊的配置有修改都弹,而哪個沒有修改娇豫。
如果只有一個App或者Library工程的話,那么就不會有用畅厢。如果你有很多Module冯痢,并且比較復雜的話,那么這個屬性可以節(jié)省很多構(gòu)建時間
Profiling
如果你想看到是那部分過程拖慢了構(gòu)建速度,那么可以在Gradle Task的時候通過添加--profile
標志位來獲取一個Profile文件浦楣。當提供了這個標志位后袖肥,Gradle創(chuàng)建出了一個Profiling Report筒主,可以從這個文件看到那部分的構(gòu)建消耗了最多的時間懊亡。
這個Report會存在/build/reports/profile
文件夾下,而類型為HTML赶盔。以下為一個執(zhí)行完多Module的構(gòu)建任務(wù)的Report:
這個Profile Report展示了每個階段執(zhí)行任務(wù)時所消耗的時間历恐。在上面的Summary是每個Module在Configuration階段所耗費的時間寸癌。而Dependency Resolution
展示了每個模塊解決依賴關(guān)系所耗費的時間。Task Execution
階段包含了執(zhí)行階段的時間弱贼。而這個里面包含了每一個任務(wù)所執(zhí)行的時間蒸苇,從高到低排序。
Jack and Jill
如果你想要使用一個實驗工具吮旅,那么Jack
和Jill
也可以提升構(gòu)建速度填渠。
Jack
:Java Android Compile Kit,它是Android Build Toolchain中的一個新工具鸟辅。她可以編譯Java代碼直接到Dex格式氛什。它有它自己的.jack
庫格式,并且處理了打包和壓縮匪凉。
Jill
:Jack Intermediate Library Linker枪眉,它是一個可以把.aar和.jar文件轉(zhuǎn)換成.jack庫的工具。不建議在Production版本中使用這兩個工具再层。
你可以把Build Tool版本提升到21.1.1以上贸铜,Gradle版本提升到1.0.0版本以上,然后在defaultConfig
代碼塊中添加屬性:
android {
buildToolsRevision '22.0.1'
defaultConfig {
useJack = true
}
}
你可以在一個buildType
或者ProductFlavor
中啟用聂受。這種方式蒿秦,你可以繼續(xù)使用常規(guī)的Build Toolchain,并且可以進行一個測試的構(gòu)建蛋济。
android {
productFlavors {
regular {
useJack = false
}
experimental {
useJack = true
}
}
}
Ignoring Lint
當你執(zhí)行一個Release構(gòu)建的時候棍鳖,Lint會檢查你的代碼。Lint是一個靜態(tài)代碼分析工具碗旅,可以標志出Java代碼以及Layout的Bug渡处。某些情況下,甚至會打斷構(gòu)建祟辟。如果你之前沒用Lint医瘫,而現(xiàn)在想在Gradle中啟用的話,Lint可能會報很多錯誤旧困。至少能夠讓構(gòu)建過程能夠正常運轉(zhuǎn)醇份,你可能會讓Gradle別處理Lint的錯誤稼锅。這只是一個臨時方案,因為忽略Lint的錯誤可能會導致App Crash僚纷。
為了將Lint錯誤導致中斷的問題避免矩距,可以禁用掉abortOnError
:
android {
lintOptions {
abortOnError false
}
}
臨時禁用可以使Ant工程可以更快的升級到Gradle中。
Advanced app deployment
之前我們通過BuildType
和Product Flavors
來構(gòu)建多個版本的APP畔濒。然而在很多情況下剩晴,我們可以通過其他方法來達到目的。
Split APK
Build Variants能夠被分成很多塊侵状,每一塊都有自己的代碼赞弥,資源,Manifest文件趣兄。APK Split绽左,從另一方面來說,只會影響App的打包艇潭。編譯拼窥,壓縮,混淆等等流程還是會共享蹋凝。這種機制允許你可以基于Density或者ABI來分割APK鲁纠。
你可以在android
的配置項中通過定義一個splits
代碼塊配置分割。為了配置density分割鳍寂,就可以創(chuàng)建一個density
代碼塊改含。如果希望按照ABI分割,則使用abi
代碼塊迄汛。
如果你啟用了density分割捍壤,Gradle會為了每個density創(chuàng)建一個單獨的APK。如果不需要density的話鞍爱,你可以手動的exclude其中的densities鹃觉,來提升構(gòu)建速度。例如:
android {
splits {
density {
enable true
exclude 'ldpi', 'mdpi'
compatibleScreens 'normal', 'large', 'xlarge'
}
}
}
如果你不只支持一些densities睹逃,你可以使用include
來創(chuàng)建一個densities的白名單盗扇。為了使用include
,首先需要使用reset()
屬性唯卖,該屬性可以重置densities的列表為一個空的字符串粱玲。
compatibleScreens
屬性是可選的,并且會在manifest文件中注入一段代碼拜轨。這個配置可以讓一個App支持大屏幕,而不支持小屏幕允青。
使用ABI
分割APK也是同樣的橄碾,所有的屬性都和density分割一樣卵沉。在執(zhí)行完density分割后的構(gòu)建結(jié)果中:
app-hdpi-release.apk
app-universal-release.apk
app-xhdpi-release.apk
app-xxhdpi-release.apk
app-xxxhdpi-release.apk
如果你希望把這些APK發(fā)布到Google Play上的話,你就需要確保每個APK都有不同的版本號法牲。這也就意味著史汗,分割必須有一個單獨的版本號。而我們可以通過applicationVariants
屬性來完成:
ext.versionCodes = ['armeabi-v7a':1, mips:2, x86:3]
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
output.versionCodeOverride = project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) * 1000000 +android.defaultConfig.versionCode
}
}
這一段代碼會檢查ABI拒垃,并且保證每一個Variant都有一個單獨的版本號停撞。