基於APK加速啟動時間的Android系統資源優化

六一發表於2021-11-03

為了儘可能減⼩應⽤的⼤⼩,我們應該在釋出版本中移除不使⽤的程式碼和資源。 另外還存在兩個 優化⽅向可以⽤來縮減應⽤程式的佔⽤空間,⼀項是使⽤混淆處理功能,該功能會縮短應⽤的類 和成員的名稱;另⼀項是使⽤優化功能,該功能會採⽤更積極的策略來進⼀步減⼩應⽤的⼤⼩。本⽂將介紹如何通過APK的資源優化來減輕應⽤程式的佔⽤空間從⽽節省⽤戶資源。

提出問題

⾸先使⽤友盟+推出的產品U-APM來實際測試應⽤程式在不同的裝置上的使⽤情況:

從圖中可以看出應⽤程式在啟動時間上還存在優化空間,下⼀步我們將讀取應⽤程式的記憶體分配,確定能從哪些⽅向⼊⼿對資源進⾏優化,從⽽加快啟動時間。

為了對以上⼏個指標進⾏詳細分析,我們使⽤了Android 窗⼝上的 Memory 選項卡, 它將向我 們顯示隨時間在堆上分配的資料量:

圖中顯示發⽣了 GC 事件, 刪除了未使⽤的物件並釋放了堆上的空間。
為了調查當前在堆中分配的內容, 我們可以使⽤左側的堆轉儲按鈕。 這將對堆中當前分配的內容 進⾏快照, 並將其顯示在 Android Studio 內的特殊報告螢幕中:

在左側,我們看到堆中例項的直⽅圖,按類名分組。對於每⼀個,都有分配的物件數量、 這些例項的⼤⼩(淺層⼤⼩)以及這些物件在記憶體中保留的⼤⼩。後者告訴我們如果這些例項被釋放 ,可以釋放多少記憶體。這個檢視讓我們對應⽤程式的記憶體佔⽤有⼀個重要的瞭解,幫助我們識別⼤ 型資料結構和物件關係。這些資訊可以幫助我們構建更⾼效的資料結構, 解開物件連線以減少保 留的資源,並最終儘可能地減少資源佔⽤。

隨後,我們使⽤單個佈局⽂件構建⼀個最⼩的 APK,以計算佈局⽂件的名稱在 Android APK 中出現次數。

使⽤ Gradle 構建 Android 應⽤程式只需要⼀個AndroidManifest.xml⽂件。 我們可以新增⼀個 虛擬佈局。

.
├── build.gradle
└── src
└── main
├── AndroidManifest.xml
└── res
└── layout
└── home_view.xml

運⾏gradle assembleRelease將產⽣⼀個只有 2, 118 位元組的釋出版 APK。 我們可以使⽤轉儲其內容xxd並查詢home_view位元組序列。


根據此輸出,在 APK 中存在 3 次未壓縮的路徑和 1 次未壓縮的僅名稱。
zip⽂件是⼀個⽂件條⽬列表, 後跟所有可⽤條⽬的⽬錄。 每個條⽬都包含⽂件路徑, ⽬錄也是 如此。 這說明了輸出中的第⼀次出現 (條⽬標題) 和最後⼀次出現 (⽬錄記錄) 。

輸出中出現的中間兩次來⾃resources.arsc⽂件內部, 該⽂件是資源排序的資料庫。 它的內容是可⻅的, 因為該⽂件在 APK 中未壓縮。 運⾏

aapt dump --values resources
build/outputs/apk/release/app-release-unsigned.apk

顯示home_view記錄及其到路徑的對映:

Package Groups (1)
Package Group 0 id=0x7f packageCount=1 name=com.example
Package 0 id=0x7f name=com.example
type 0 configCount=1 entryCount=1
spec resource 0x7f010000 com.example:layout/home_view: flags=0x0000000
config (default):
resource 0x7f010000 com.example:layout/home_view: t=0x03 d=0x0000000
(string8) "res/layout/home_view.xml"

APK 包含classes.dex⽂件中第五次出現的名稱。 它沒有顯示在xxd輸出中, 因為⽂件被壓縮了。 運⾏

baksmali dump <(unzip -p build/outputs/apk/release/app-release-unsigned.apk classes.dex)

顯示 dex ⽂件的字串表, 其中包含以下條⽬home_view

這是⽤於將R.layout佈局名稱對映到唯⼀整數值的類中的欄位。 順便說⼀下,該整數是resources.arsc資料庫的索引, ⽤於查詢相關⽂件名以讀取其 XML 內容。
總結⼀下我們問題的答案,對於每個資源⽂件,完整路徑出現 3 次,名稱出現兩次。

優化資源

Android Gradle 外掛 4.2 引⼊了⼀個android.enable ResourceOptimizations=true標誌, 它將運⾏針對資源的優化。這會aapt optimize在合併的資源和resources.arsc⽂件打包到 APK 之前 調⽤它們的命令。 優化僅適⽤於釋出版本,⽆論是否minifyEnabled設定為 true,都會運⾏ 。新增標誌後,gradle.properties我們可以使⽤漫反射來⽐較兩個APK以檢視其效果。 輸出很⻓, 所以我們將按部分分解。


⾸先是 APK 中內容的差異。“壓縮”列是 APK 內的成本,“未壓縮”列是提取時的成本。

該res類別代表我們的單個資源⽂件,其⼤⼩下降了28個位元組。 該arsc類別⽤於resource.arsc 本身, 顯然,這⾥產⽣了⼀定的優化。

這兩部分代表資源資料庫的程式碼和內容。沒有變化,我們可以推斷優化沒有影響
R.layout.home_view 欄位和 home_view 資源條⽬。

最後顯示了優化的效果。我們的佈局資源的⽂件名被明顯截斷並移出layout/⽂件
夾。

Gradle 項⽬中, XML 的⽂件夾和⽂件名是有意義的。 ⽂件夾是資源型別, 名稱對應.arsc⽂件中⽣成的欄位和資源條⽬。 但是,如果這些⽂件位於 APK 中, ⽂件路徑就變得毫⽆意義。資源優化通過使名稱儘可能短來進⾏優化。

輸出 aapt dump 資源資料庫也反映了⽂件更改:

Package Groups (1)
Package Group 0 id=0x7f packageCount=1 name=com.example
Package 0 id=0x7f name=com.example
type 0 configCount=1 entryCount=1
spec resource 0x7f010000 com.example:layout/home_view: flags=0x0000000
config (default):
resource 0x7f010000 com.example:layout/home_view: t=0x03 d=0x0000000
(string8) "res/eA.xml"

APK 中路徑的所有三個出現現在都更短, 從⽽節省了 36 位元組。 雖然 36 位元組是⼀個⾮常⼩的 數字, 但整個⼆進位制⽂件只有 2, 118 位元組。 36 位元組的節省了 1.7% 的資源。
Nick Butcher Plaid應⽤程式有 734 個資源⽂件。

除了數量之外,資源⽂件的名稱更具描述性 (這是說它們更⻓的⼀種奇特⽅式) 。 home_viewPlaid 包含的名稱是 searchback_stem_search_to_back.xmlattrs_elastic_drag_dismiss_frame_layout
designer_news_story_description.xml

沒有資源優化的構建與啟⽤它的構建進⾏⽐較:

資源優化使 APK ⼤⼩節省了0.76%。

Uwe TrottmannSeriesGuide應⽤程式有 1044 個資源⽂件。與Plaid 不同,它沒有本機庫,這應該會讓優化效果更好。我再次將項⽬更新到 AGP 4.2 並⽐較兩個版本:

在這⾥,資源優化能夠將 APK ⼤⼩減少2.0%!
Chris Banes 的Tivi應⽤程式有⼀個使⽤Jetpack Compose 編寫的重要⼦集,這意味著整體資源更少。當前構建仍包含 776 個資源⽂件。

通過使⽤ Compose,Tivi 已經在使⽤最新的 AGP 4.2。 通過兩個快速構建,我們可以看到資源優化的影響:

我們再⼀次達到了 APK ⼤⼩縮減 2.0% 的程度

APK簽名

APK 簽名有多個版本,如果您的版本minSdkVersion低於24,則需要在簽名時包含版本V1 。V1 簽名使⽤Java 的.jar 簽名規範,該規範將每個⽂件作為⽂件中的⽂本條⽬單獨簽名META-INF/MANIFEST.MF

在為原始單佈局應⽤程式建立和配置金鑰庫後,轉儲清單⽂件

unzip -cbuild/outputs/apk/release/app-release.apk META-INF/MANIFEST.MF

顯示以下簽名:

Manifest-Version: 1.0
Built-By: Signflinger
Created-By: Android Gradle 4.2.0-alpha08

Name: AndroidManifest.xml
SHA-256-Digest: HdoGVd8U3Zjtf2VkGLExAPCQ1fq+kNL8eHKjVQXGI60=

Name: classes.dex
SHA-256-Digest: BVA1ApPvECg56DrrNPgD3jgv1edcM8VKYjcJEAG4G44=

Name: res/eA.xml
SHA-256-Digest: nDn7UQex2OWB3/AT054UvSAx9pYNSWwERCLfgdM6J6c=

Name: resources.arsc
SHA-256-Digest: 6w7i2Z9+LjwqlXS7YhhjzP/XhgvJF3PUuyJM60t0Qbw=

每個⽂件的完整路徑出現,使每個資源路徑的總出現次數達到四次。由於較短的名稱將再次導致 此⽂件包含更少的位元組, 因此資源優化在簽名中具有更⼤的影響。

根據資料顯示, 這⼀⽅法可以節省 1-3% 的APK ⼤⼩。 根據實際測試 這個範圍似乎是正確的。最終節省的費⽤將取決於 APK 中資源⽂件的⼤⼩和數量。

相關文章