Android 編譯耗時優化:深入理解增量編譯提高開發效率 增量編譯簡介 Android 編譯打包流程 增量編譯原理 有贊 Android 編譯方案Savitar

增量編譯簡介

增量編譯是相對全量編譯而言的。所謂增量編譯,是指當源程序的局部發生變更後進重新編譯的工作只限於修改的部分及與之相關部分的內容,而不需要對全部代碼進行編譯。增量編譯對軟件開發,尤其是在調試期,可以大大縮短編譯時間, 提高編譯效率。

而全量編譯指的是,當用戶源程序被局部修改後重新編譯代碼會涉及全部源代碼,並不只限於局部修改及其相關部分。換句話說,無論修改了什麼,全量編譯都將進行一次全新的完整的編譯,並不基於上一次的編譯基礎。

一般來說,在軟件開發中,全量編譯用於版本的構建與發佈,比較耗費時間和資源。而處於調試階段的程序,一般都採用增量編譯,這樣對於問題的定位和解決都比省時省力。在Android開發中,隨着工程代碼量膨脹,編譯耗時也越來越長,拖慢了開發效率,因此Android官方推出了Instant Run和Apply Changes等增量更新的方案。

Instant Run簡介

Instant Run是Android Studio 2.0版本推出的一個增量編譯功能,使用Instant Run功能時,需要在build.gradle 文件中將 minSdkVersion 設置爲 15 或以上時,並且爲另外獲得最佳性能,可以將 minSdkVersion 設置爲 21 或更高。

之前在Android Studio 3.0版本,gradle爲2.14.1的版本中做過一個測試,編譯一個簡單的Demo項目從之前的10秒降低到大概2、3秒。默認情況下,Instant Run是關閉的,如果要開啓Instant Run,可以在Settings中打開Instant Run,如需所示。


不過,Android Studio在3.5版本廢棄了Instant Run,並使用HotSwap替代了Instant Run,如下圖所示。


關於Instant Run的一些原理方面的內容,可以參考我之前的文章介紹:深入理解Android Instant Run運行機制

Apply Changes

在Android Studio 3.5及其以上版本,官方提供了Apply Changes,使用Apply Changes時,需要滿足以下兩個條件:

  • apk必須是debug包;
  • Android 8.0及以上的手機上運行

當我們使用Android Studio運行項目後,會在菜單欄看見3個按鈕,分別用來控制應用重啓,如下圖所示。

如上圖所示,從左到右的按鈕分別表示【Run】、【Apply Changes 】和【Apply Code Changes】。

  • Run:將部署所有的變化並重啓應用 。
  • Apply Changes: 將嘗試應用變化的資源和代碼,並僅重啓Activity而不需要重啓整個應用。
  • Apply Code Changes :將嘗試在不重啓操作的情況下應用變化的代碼,如果只有代碼修改,可以使用此按鈕來使代碼生效。

不過,由於Apply Changes僅支持在Android 8.0 或者更高版本的手機上運行,並且實際操作時在工程中帶來的提速效果也不明顯。

Freeline

除了官方的方案外,阿里巴巴客戶端團隊還基於動態替換研發了一款針對Android平臺的增量編譯工具,它可以充分利用緩存文件,在幾秒鐘內迅速地對代碼的改動進行編譯並部署到設備上,有效地減少了日常開發中的大量重新編譯與安裝的耗時。

性能方面:內部採用了類似Facebook的開源工具buck的多工程多任務併發思想:端口掃描,代碼掃描,併發編譯, 併發dx,併發merge dex等策略,在多核機器上有明顯加速效果,另外在class及dex,resources層面作了相應緩存策略,做到真正增量開發,另外引入並優化 buck的部分加速組件dx,DexMerger,資源編譯方面,深入改造了Aapt資源編譯流程,當資源發生改變時候,秒級完成增量包編譯,其中增量包 僅含最小的變更集合(10Kb~數百Kb內),後期也被運用到線上進行資源/代碼動態替換。相比目前instant- run,buck,layoutcast等方案快數倍速度。

不過,Freeline同樣存在着一些不可忽視的問題。首先是不支持Kotlin,這在Kotlin已經被谷歌官宣爲Android開發首選語言的今天,是比較致命的。另外,不支持刪除帶id的資源,否則可能導致資源編譯流程出錯。

另外一個潛在的問題是,爲了確保編譯速度,Freeline是犧牲了一部分正確性的。例如,在改動公有靜態常量的時候,只會編譯對應的類文件,而引用到該常量的其他類,並不會參與編譯的。由於常量內聯優化的存在,就可能導致這些類在運行時,使用的仍然是舊的值,進而出現改動不生效的問題。

Android 編譯打包流程

對於Android是如何從源碼到安裝包的過程,可以參考Android官方給的一幅圖,主要會經歷編譯、鏈接和簽名等操作。


上面展示的是Android源碼編譯成安裝包的過程,而增量更新的完整的流程是: 【修改代碼】->【編譯工程】->【安裝APK】->【運行驗證】。

對於編譯階段,首先是收集工程中的所有資源文件進行編譯,得到資源包以及資源索引類。隨後資源索引類會跟隨工程的所有源代碼文件一起被編譯爲字節碼文件,並且字節碼文件還需要被進一步編譯爲Dex文件,這樣才能被Android虛擬機所識別。

Android的編譯打包會分爲以下幾個階段:

  • R文件的生成:R文件記錄了每個資源的ID,之後要參與到java的編譯過程,R文件是由aapt(Android Asset Package Tool)生成。
  • Java(Kotlin)源代碼:我們知道有時app開發中會跨進程通信,這時可以通過aidl的方式定義接口,aidl工具可以根據aidl文件生成對應的java文件。之後R文件、aidl相關java文件、src中的java文件通過編譯生成 .class文件。
  • dex生成:編譯後的.class會又由dex工具打包成dex文件,其中,Android增量打包工具freeline中用到了Buck中提取的dex工具,freeline給出的數據是比原生的dex工具快了40%

資源文件編譯

  • aapt(Android Asset Package Tool):aapt工具對app中的資源文件進行打包和歸檔。

下圖完整的演示了Android編譯期和運行期的整個步驟。


增量編譯原理

Android增量編譯分爲代碼增量和資源增量,Android早期的Instant Run方案在資源上並不是增量的,而是把整個應用的資源打成資源包,推送至手機的,因此效率極低。

代碼編譯

谷歌在支持multidex之後(即典型的65535問題),Android打包後會存在多個dex文件,運行時加載類時,會從一個dexList依次查找,找到則返回,利用這個原理可以把增量的代碼打包成dex文件,插入到dexList的前邊,這樣就可以完成類的替換。

關於代碼的增量編譯需要考慮兩個主要的問題,即獲取改動文件並進行編譯、對依賴的代碼進行編譯。關於代碼的增量編譯,可以參考QQ音樂的增量編譯方案:QQ音樂Android編譯提速之路

資源編譯

資源編譯與代碼增量是類似的,即先收集被改動的資源,然後進行編譯。Android的資源編譯主要使用的是aapt或者aapt2。一般來說現在都是使用aapt2來進行資源的打包編譯,因爲aapt工具是不支持單個資源編譯的。

aapt2(Android 資源打包工具)是Android Studio 和 Android Gradle 插件使用它來編譯和打包應用的資源構建工具。aapt2 會解析資源、爲資源編制索引,並將資源編譯爲針對 Android 平臺進行過優化的二進制格式。

使用aapt2進行資源打包編譯時,分爲編譯(compile)與鏈接(link) 兩步,在編譯階段,負責將單個或者多個資源編譯爲二進制文件;鏈接階段,則負責合併所有二進制文件再打包。

關於資源的增量編譯,可以參考QQ音樂的增量編譯方案:QQ音樂Android編譯提速之路

Android發展到現在,前面很多的增量編譯方案都已經失效,現在市面上比較成熟的就是QQ音樂團隊的動態編譯方案有贊 Android 增量編譯方案Savitar

有贊 Android 編譯方案Savitar

當項目經過多次迭代之後,就會遇到各種各樣的問題,而編譯慢是每個成熟 Android 團隊都無法迴避的問題。在之前有贊零售 Android 團隊的技術分享中,整個Android項目有 25 個業務模塊,擁有 45W+ 行源代碼(Java + Kotlin)以及多個構建 Flavor。小夥伴在進行需求開發時,平均的增量編譯構建時間達到了兩分鐘,再加上一些 Gradle 配置與APK安裝過程,基本上驗證一行代碼的修改需要近三分鐘(MacBook Pro 13-inch, 2016, i5-8G),這樣的情況大大降低了團隊的開發效率。

在 Savitar 誕生之前,我們嘗試了社區中一些成熟的解決方案,如 BUCK、Freeline、InstantRun 等知名框架。不過調研下來,都或多或少的存在一些問題。

比如FaceBook的BUCK框架,自身有強大的構建系統,通過增量構建緩存機制,可以有效提升編譯的速度,但是其使用和配置過於複雜,對於工程的入侵比較大,且對於一些 Databinding、 Kotlin 等 Android 的特性支持還有欠缺。

其次是阿里巴巴開源的Freeline ,Freeline以其極快的部署速度出名,但對我們來說致命缺點是不支持 Kotlin。

InstantRun 是 Google 推薦的加速方式,擁有最全面的支持性,但由於我們是多進程的工程,並且 InstantRun 在編譯時的一些準備 Task 也會消耗一些時間,在實踐過程中發現加速並不明顯。

方案實現

Savitar 是有贊 Android 團隊增量編譯提效方案,它能夠有效減少模塊修改編譯時間,包含配套 IDE 插件,使用方便,具有如下一些顯著的特點。

  • 支持Java、Kotlin的增量編譯
  • 支持layout、values、assets、images等資源文件的增量編譯
  • 提供GUI界面插件
  • 基於 Git調試、多分支管理,可以根據實際情況更換分支

結構設計

如圖所示,Savitar 整體分成四個部分:

  • GUI 插件部分:面向使用者的 GUI 界面,內部包含了可運行 Jar(以下簡稱 Runner)的自動更新、各種檢查任務、編譯腳本調用執行
  • Runner 部分:一個 Jar 包,包含 Savitar 核心邏輯代碼,完成修改獲取、腳本生成、編譯執行等任務
  • 工程支持部分:一個 Gradle 插件,完成對工程信息的獲取和產物加載代碼的插入
  • 外部依賴部分:完成整個流程所需要的外部依賴程序

下圖演示了Savitar從代碼修改到完成修改產物加載運行的完整過程。

可以發現,從代碼修改到完成修改產物主要經歷了以下幾個步驟:

  1. 獲取改動信息:獲取代碼和資源修改,是整個過程的前提
  2. 獲取工程信息:獲取當前工程的依賴信息,目錄信息和 Git 信息,爲後續編譯做準備
  3. 編譯生成產物:進行代碼、資源編譯,生成 Dex 產物和 Apk 產物
  4. 重啓加載產物:完成對編譯產物的加載運行,完成整個加速過程

關於Savitar是如何從獲取改動信息到完成加載,可以參考Android 增量編譯提效方案Savitar的詳細介紹。

如何使用

爲了方便開發者使用Savitar實現增量更新,Savitar 開發了一款 IDE 插件,只需要一鍵觸發就可以完成整個編譯打包流程。首先,打開Android Studio ,然後依次選擇 【Preference】 -> 【Plugin】 搜索Savitar並安裝,如下圖所示。


安裝完成後重啓 IDE,然後在 Android Studio 中工具欄就會出現 Savitar 的圖標,如下圖所示。


點擊圖標後,可以在 Savitar Window 看到工具編譯、打包、推送整個運行過程,包含錯誤信息,如下圖所示。

常見文件解答

是否支持,如何支持Kotlinx ?

如下所示,有下面一段代碼:

import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        buttonCpu.setOnClickListener {
            ... //點擊事件
        }
    }
}

對於上面的代碼,想必使用過 Kotlin 的 Android 同學並不會陌生,利用 Kotlinx 特性,可以在 .kt 代碼中使用 Xml 中定義過組件Id直接獲取 View 實例進行操作,極大減少 UI 開發成本。

但是上面代碼中的 import 並不是一個普通的形式,這樣的語法如果直接使用標準 kotlinc 進行編譯,會出現找不到 import 錯誤。此時需要藉助到 Kotlin 編譯器插件,在 Kotlin 編譯時傳入 Kotlinx 對應插件的 Jar 地址和參數,就可以完成包含 Kotlinx 語法的文件編譯。

sh kotlinc  
-Xplugin=lib/android-extensions-compiler.jar
-P plugin:org.jetbrains.kotlin.android:package=${package_name}
-P plugin:org.jetbrains.kotlin.android:variant='${flovar};${resource_package}'

作者:xiangzhihong
鏈接:https://juejin.cn/post/6895350206608556040

筆記

【360°全方位性能調優】

這份筆記我將Android-360°全方位性能優化知識點,以及微信、淘寶、抖音、頭條、高德地圖、優酷等等億萬級用戶APP在性能優化方面的實踐經驗,整合成了一套系統的知識筆記PDF,從理論到實踐,涉及Android性能優化的所有知識點,長達721頁電子書!相信看完這份文檔,你會對Android性能調優知識體系及各種方案有更系統、更深入的理解。

需要的小夥伴點贊+關注後我的GitHub即可直接免費下載獲取~

文末

感謝大家關注我,分享Android乾貨,交流Android技術。
對文章有何見解,或者有何技術問題,都可以在評論區一起留言討論,我會虔誠爲你解答。
也歡迎大家來我的B站找我玩,有各類Android架構師進階技術難點的視頻講解,助你早日升職加薪。
B站直通車:https://space.bilibili.com/544650554

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章