各位戰士,歡迎來到第二十三天的戰場。在昨天的任務中,我們學會了如何最適化地管理圖片這項最重的「軍糧」。今天,我們的目標將從單個資源轉向整個「行軍包裹」——也就是我們的 APK 檔案。
一個臃腫的 APK,就像士兵身上沉重的行囊,會直接影響作戰效率。Google Play 的數據明確顯示,APK 的大小每增加 6MB,應用程式的安裝轉換率就會下降 1%。在網路環境不佳或儲存空間有限的地區,一個小巧的 APK 是贏得使用者的第一道關卡。
今天的任務,就是學習如何使用 Android 內建的最強大的瘦身武器——R8 和資源壓縮——來為我們的應用程式減負。
在動手優化之前,我們必須先了解我們的敵人——APK 的組成結構。知己知彼,方能百戰不殆。Android Studio 內建的 APK Analyzer 就是我們的偵察機。
打開後,你會看到 APK 內部檔案的詳細分佈:
classes.dex
: 這是我們所有 Kotlin/Java 程式碼被編譯後的檔案。它是 R8 的主要戰場。res/
: 包含我們所有的資源檔案,如圖片、佈局、字串等。它是資源壓縮的主要戰場。lib/
: 包含 .so
結尾的原生函式庫(C/C++ 程式碼)。assets/
: 你放入的原始資產檔案。AndroidManifest.xml
: 應用程式的清單檔。優化的第一步,永遠是測量。APK Analyzer 讓我們清楚地看到是哪部分佔用了最大的空間,從而指導我們優化的方向。
R8 是 Android 現代化的程式碼處理工具(ProGuard 的繼任者),當你在 Release 版本中啟用它時,它會自動執行三項關鍵任務:
a.b.c()
)。這樣做有兩個好處:一是讓你的程式碼極難被反編譯和破解;二是極大地減少了 .dex
檔案的大小,因為短名稱佔用的空間更少。如何啟用?
在你的 :app
模組的 build.gradle.kts
(或 build.gradle
) 檔案中,找到 release
建構類型,修改如下:
// In app/build.gradle.kts
android {
//...
buildTypes {
getByName("release") {
isMinifyEnabled = true // 這一行就啟動了 R8 的所有功能
isShrinkResources = true // 我們稍後會講到這個
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
注意 proguard-rules.pro
R8 的靜態分析無法處理「反射 (Reflection)」這種動態呼叫的程式碼。如果你使用了像 Gson/Moshi 這類序列化函式庫,就需要手動在 proguard-rules.pro
檔案中添加 -keep
規則,告訴 R8:「不要移除或重命名這個類別,我會在執行期透過反射用到它!」
好消息是,現在絕大多數主流函式庫都會自動將自己的 keep 規則打包進來,我們需要手動設定的情況已經越來越少了。
shrinkResources
—— 壓縮無用資源R8 只處理程式碼,那專案裡那些早就沒人用的圖片、佈局、字串等資源該怎麼辦?這就是 isShrinkResources = true
發揮作用的時候。
它的工作原理:
資源壓縮依賴於程式碼壓縮。在 R8 完成它的工作,移除了所有無用程式碼之後,資源壓縮工具會開始掃描剩餘的程式碼,檢查有哪些資源(如 R.drawable.my_icon
)被引用了。任何完全沒有被引用的資源檔案,都會被從最終的 APK 中安全地移除。
如上所示,在 release
build type 中,確保 isMinifyEnabled
為 true
的前提下,再將 isShrinkResources
設為 true
。
注意動態資源
與 R8 類似,如果你是透過拼接字串的方式動態獲取資源(例如 resources.getIdentifier("icon_" + name, ...)
),壓縮工具也無法偵測到。這時,你需要在 res/raw/keep.xml
檔案中手動聲明需要保留的資源。
今天,我們打響了 APK 瘦身的第一場戰役,掌握了兩種最有效、也是最基礎的瘦身技術:
啟用 R8 (isMinifyEnabled = true
):透過壓縮、混淆和優化,從根本上縮減 classes.dex
檔案的大小,並提升程式碼安全性。
啟用資源壓縮 (isShrinkResources = true
):在 R8 的基礎上,自動移除專案中所有未被引用的資源檔案。
使用 APK Analyzer:在優化前後對比 APK 的大小和組成,量化我們的戰果。
我們已經成功地為 APK 的「內容」進行了瘦身。但是,現代化的戰爭不僅僅是武器本身,更是「投放方式」的革新。明天,我們將學習更進階的瘦身術:【App Bundles 與動態交付】,它將徹底改變我們交付應用的方式,為使用者帶來極致的大小優化。
我們明天見!