納尼?我的Gradle build編譯只要1s

coder-pig發表於2018-11-30

為了避免被Diss,先宣告瞭,本文不算原創,只是對幾篇Gradle編譯提速的文章進行了: 搬運整理細化和補漏,文尾已列出相關的參考文獻!先上個圖舒服下:

納尼?我的Gradle build編譯只要1s

(PS:這是直接新建的專案通過各種折騰後編譯的時間,筆者公司的專案折騰完耗時5s左右, 但是因為相容要改的東西多,升級gradle等操作並沒弄,不過也是效果明顯,真的可以試試~)

納尼?我的Gradle build編譯只要1s

大部分的Android開發仔吐槽的最多的基本都是等Gradle編譯。

納尼?我的Gradle build編譯只要1s

編譯可以說是日常操作最頻繁的,在調UI,除錯改Bug時候達到一個峰值,每改點東西, 就build一下。可以說是相當可怕,我們來粗略的算一筆賬:

假設編譯一次要3分鐘,一天編譯40次

  • 每天花費:2小時等待編譯
  • 一週花費:14小時等待編譯
  • 一個月花費:60小時等待編譯 => 2.5天

每個月花2.5天是在等待Gradle編譯,多呆哦,所以,讓Gradle編譯提速,顯得格外重要, 本節就從方方面面說下減少這個等待的時間吧。


1.升級下你的電腦配置

大部分的Gradle編譯卡和慢,都是因為電腦配置的原因,個人感覺關鍵基本配置如下:

  • CPU:i5系列就夠了
  • 記憶體:重要!至少要8G,有條件建議上16G
  • 硬碟:重要!必須是SSD固態硬碟,窮一點的上128G,稍微寬裕點上256

其他比如顯示卡啊啥的沒什麼太大影響,配置肯定是越高越好,在自己的經濟承受範圍以內即可。


2.調整AS的記憶體分配

電腦配置差不多了,接下來就要調整下AS的可使用記憶體,開啟AS在右下角可以看到使用記憶體和分配記憶體,如圖所示:

納尼?我的Gradle build編譯只要1s

如果你的AS裡沒有這個圖的話,可以依次點選選單欄的:「File」->「Settings」->「Appearance % Behavior」-> 「Appearance」-> 找到如圖所示的「Show memory indicator」勾選,然後點選OK,就可以在狀態列看到記憶體分配資訊了。

納尼?我的Gradle build編譯只要1s

接著我們來修改AS的記憶體分配,來到AS的安裝目錄下,進入bin目錄,找到:

納尼?我的Gradle build編譯只要1s

用文字編輯類軟體開啟對應檔案,內容如下:

-Xms256m
-Xmx1280m
-XX:ReservedCodeCacheSize=240m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djna.nosys=true
-Djna.boot.library.path=

-da

複製程式碼

接著我們修改下前三項,改成更大的值:

-Xms512m    //JVM啟動的起始堆記憶體,堆記憶體是分配給物件的記憶體
-Xmx2560m   //Java虛擬機器啟動時的引數,用於限制最大堆記憶體
-XX:ReservedCodeCacheSize=480m  //JIT java compiler在compile的時候的最大程式碼快取
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djna.nosys=true
-Djna.boot.library.path=
複製程式碼

修改完後儲存,然後回到AS中,依次點選選單欄:「File」->「Ivalidate Caches/Restart...」,點選後出現如圖對話方塊,點選「Ivalidate Caches/Restart...」。

納尼?我的Gradle build編譯只要1s

然後等待AS重啟即可。然後右下角可以看到可用記憶體已經發生了改變,AS明顯開啟流暢多了。

納尼?我的Gradle build編譯只要1s


3.增加Gradle的堆大小並啟用dex-in-process

Dex-in-process允許你dex和gradle構建在同一個程式進行,以此加快增量構建和乾淨構建的速度。 要使用dex-in-process非常簡單:確保 grale 程式有足夠的記憶體分配給 dex 步驟來完成整個操作。 建議的公式:

Gradle memory >= Dex memory + 1Gb !
複製程式碼

就是分配給gradle的記憶體比dex的記憶體多1g,dex程式記憶體設定在開發module的 build.gradle裡面控制。新增如下內容:

納尼?我的Gradle build編譯只要1s

除此之外,dexOptions中還可以新增下述配置:

//使用增量模式構建 
incremental true 

//是否支援大工程模式 
jumboMode = true 

//預編譯 
preDexLibraries = true 

//執行緒數 
threadCount = 8 
複製程式碼

配置完dex,接著要配置gradle,開啟「gradle.properties」修改org.gradle.jvmargs 的大小,按照公式,多1g,所以這裡配置成2g:

org.gradle.jvmargs=-Xmx2g
複製程式碼

除此之外設定下JVM最大允許分配的非堆記憶體,以及堆記憶體移除時輸出堆的記憶體快照。

org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=0.5g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
複製程式碼

接著可以再Terminal中中鍵入gradlew clean build來看看構建的速度是否加快了。 一般dex設定為1-2G,gradle設定為2-4G就可以了,分配更多的記憶體並不會使構建加快多少!


4.其他gradle.properties配置

除了配置給gradle分配的記憶體外,還可以通過新增下述配置使得構建加快:

#開啟守護執行緒
org.gradle.daemon=true
    
#開啟並行編譯任務
org.gradle.parallel=true
    
#開啟快取
android.enableBuildCache=true
複製程式碼

另外,你還可以在下述目錄中建立一個gradle.properties檔案,全域性生效,就不用每個專案都另外配置了, 當然你喜歡還是可以在專案中建立這個檔案。

納尼?我的Gradle build編譯只要1s


5.配置依賴包下載地址

就是使用阿國內阿里雲的依賴下載地址替換Google依賴包下載地址,開啟Project級別的build.gradle檔案,新增阿里雲的地址:

repositories {
    maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
    ...
}

allprojects {
    repositories {
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
        maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
        ...
    }
}    
複製程式碼

6.離線編譯

如果你的專案比較穩定,沒有什麼新的依賴(第三方庫引用),可以使用這種方法來提升編譯速度。 依次點選選單欄「Setting」->「Build」-> 「Gradle」,找到如圖的「Offline work」勾選,點選OK即可。

納尼?我的Gradle build編譯只要1s

也可以不配置,在命令列編譯的時候加上**--offline**,比如:

gradlew build --offline
複製程式碼

7.使用靜態依賴項版本

在引用依賴庫的時候,儘量避免使用+這種動態版本號,而儘量使用靜態編碼編碼版本號, 如果用動態版本號,每次編譯的時候Gradle會去檢查是否有更新,比如:

# 動態版本號
com.android.tools.build:gradle:3.2.+

# 靜態版本號
com.android.tools.build:gradle:3.2.1
複製程式碼

8.禁用耗時但在Debug時不需要的Task

你可以通過下述兩種方式,檢視編譯的時候都執行了哪些Task,以及他們的耗時。

方法一:通過gradlew --profile

編譯命令後加上--profile,比如 gradlew build --profile,會輸出打包過程的profile report

納尼?我的Gradle build編譯只要1s

接著按照給出的路徑找到對應的html檔案,用瀏覽器開啟,點選「Task Execution

納尼?我的Gradle build編譯只要1s

然後就可以看到所有執行的Task和所需的耗時。除此之外,還可以

方法二:使用build scan(構建審視)

Gradle 官方推出的一個視覺化診斷工具,官網地址:gradle.com/build-scans…,不同的Gradle版本對應的 build-scan-plugin的版本也不同,可以在官網檢視對應的版本號:Gradle Enterprise version compatibility, 比如我的4.6對應最低版本的build scan工具的版本號為:1.8。接著在專案級別的build.gradle檔案中新增相關程式碼:

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "com.gradle:build-scan-plugin:1.8"
  }
}

apply plugin: "com.gradle.build-scan"
複製程式碼

接著命令鍵入:gradlew build --scan編譯完會問你是否Push到gradle.com,鍵入: yes即可。

納尼?我的Gradle build編譯只要1s

如果不想每次都鍵入yes,在build.gradle中新增下述程式碼:

buildScan {                     
    licenseAgreementUrl = 'https://gradle.com/terms-of-service'
    licenseAgree = 'yes'
}
複製程式碼

接著點選開啟連結,會讓你輸入一個接收報告的郵箱:

納尼?我的Gradle build編譯只要1s

接著會受到一個郵件,點開就能看到本次編譯的相關資訊了,比如這裡看到佔較多編譯時間的Task是lint:

納尼?我的Gradle build編譯只要1s

行吧,從上面的兩個例子我們可以明顯的看到lint耗時不少,而Lint在Debug階段並不需要,我們可以下述三種方式來禁用:

  • 1.通過Grale編譯引數禁用
gradlew build -x lint -x lintVitalRelease
複製程式碼
  • 2.Gradle指令碼中動態新增編譯引數

專案級別的build.gradle中buildScript新增下述程式碼:

gradle.startParameter.excludedTaskNames.add('lint')
gradle.startParameter.excludedTaskNames.add('lintVitalRelease')
複製程式碼
  • 3.永久禁用lint

在模組級別的build.gradle中的

apply plugin: 'com.android.application'
複製程式碼

前加上下述程式碼:

tasks.whenTaskAdded { task ->
    if (task.name.contains("lint") || task.name.contains("lintVitalRelease")) {
        task.enabled = false
    }
}
複製程式碼

9.一些其他的小點

① 減少本地庫依賴

Gradle在編譯時會執行大量Task,生成很多中間檔案,會導致磁碟IO擁堵,造成編譯變慢, 可以減少本地庫依賴,多使用aar進行依賴。

② 修改minSdkVersion>=21

如果你不需要相容低版本的裝置的話,可以把minSdkVersion改為21以上(Android 5.0),使用ART,在Build時只做class to dex, 而不做mergeing dex,同樣會節省一點的時間。

③ 使用implementation替代compile

注:Gradle版本需大於3.4,示例如下:

compile 'com.android.support:appcompat-v7:28.0.0'
# 改為:
implementation 'com.android.support:appcompat-v7:28.0.0'
複製程式碼

同樣的替換還有:

  • testCompile -> testImplementation
  • androidTestCompile -> androidTestImplementation
  • provided -> compileOnly

④ 將影像轉換成 WebP

有效減少圖片檔案大小,不必執行構建時壓縮,從而加快構建速度,如果你的APP用到大量圖片資源的話,效果明顯。

⑤ 停用 PNG 處理

如果不想把圖片替換成WebP,可在每次構建應用時停用自動影像壓縮的方式加快構建速度。模組級別build.gradle新增下述程式碼:

android {
  ...
  aaptOptions {
    cruncherEnabled false
  }
}
複製程式碼

⑥ 按需編譯而不是無腦build

直接**gradlew build和執行gradlew assemble** 會同時編譯生成Debug和Release的包,在除錯階段其實 我們可以使用:

gradlew assembleDebug
複製程式碼

來只編譯Debug版本的包,除此之外還可以用另一個命令編譯完直接安裝到裝置上:

gradlew installDebug
複製程式碼

同理對應Release的命令為:

gradlew assembleRelease
gradlew installRelease
複製程式碼

行吧,關於Gradle編譯提速大概就這些,有補充的歡迎在評論區留言,謝謝~

納尼?我的Gradle build編譯只要1s


參考文獻


Join in

歡迎大家加入開發交流群一起討論學習,可以新增下述的機器人小號 RobotPig,傳送『選單』 輸入對應的提示,加入對應的開發群(Android或Python),或者在公眾號『摳腚男孩』中傳送加群~
半自動拉人,會有延時,請諒解~

機器人小號
     


相關文章