要盡可能減小 APK 文件,我們應該啟用壓縮來移除發布構建中未使用的代碼和資源。
1. 使用 ProGuard 混淆代碼
在 Android 中代碼混淆和壓縮都是通過 ProGuard 來實現的,ProGuard 會檢測和移除代碼中未使用的類、字段、方法和屬性,除此外還可以優化字節碼,移除未使用的代碼指令,以及用短名稱混淆類、字段和方法。
在 build.gradle 中,使用 minifyEnabled 屬性來開啟代碼混淆:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}
2. 使用 shrinkResources 壓縮資源
在 build.gradle 中使用 shrinkResources 屬性來開啟資源壓縮,它在構建 apk 時可以移除那些沒有引用到的資源文件,通常它必須與 minifyEnabled 屬性一起使用:
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
需要注意的是,開啟該屬性設置后,它并不會移除 values/ 文件夾中定義的資源,例如:字符串、顏色、樣式等等。
3. 混淆壓縮對比
為了鑒定一下代碼混淆與資源壓縮到底有多大作用,我們隨便找個簡單的 app 工程來實驗一下。
minifyEnabled | shrinkResources | apk大小 |
---|---|---|
false | false | 4,285,579 字節,約4.3M |
true | false | 3,781,347 字節,約3.8M |
true | true | 3,779,525 字節,約3.8M |
false | true | 無法編譯 |
從中我們可以得到一些結論:
- 開啟代碼混淆和資源壓縮后,apk 大小減少了約0.5M,一個小工程都尚且如此,在一個比較龐大的工程中開啟這倆選項,壓縮的大小還是很可觀的。
- 開啟資源壓縮與不開啟相比,只減少了約2000多字節的大小,可見大頭還是靠代碼混淆,資源壓縮只能起到錦上添花的作用。
- 關閉代碼混淆開啟資源壓縮,你會發現無法編譯,Android Studio 會提示你這倆必須配對使用,要開啟資源壓縮必須得開啟代碼混淆。
4. 資源壓縮會保留文件名不保留內容
我們再做個測試,在應用的資源中放一張png圖片姑且命名為 test.png,放一個layout布局文件命名為test.xml,并且確保這2個資源文件沒有任何代碼會引用,我們開啟代碼混淆和資源壓縮打包生成apk文件。
生成apk文件后,直接將apk包拖到 Android Studio 中,可以查看apk包的相關信息,細心一點的話你會發現幾個有趣的現象:
- 在圖片資源文件夾中,仍然可以看到名為 test.png 的圖片,但是你打開看到的是一個 1*1 的圖片;
- 在 layout 文件夾中,仍然可以看到名為 test.xml 的文件,打開看到的是一個空的xml文件,沒有任何xml節點信息;
也就是說,資源壓縮后,并不是直接刪除了沒有用到的資源文件,而是生成了對應的占位文件,這些占位文件都是空文件,相比原文件大小可以忽略不計。這樣做的原因是:每個資源文件都對應 R.class 里的一個資源 id ,資源 id 的映射關系會打包在 resources.arsc 文件里,如果直接刪除了資源文件,則可能需要同時修改 resources.arsc 里的資源映射表,這樣是很繁瑣的過程,而用占位文件來替換則避免了這種情況。
5. 使用 resConfigs 去除多余的語言包
目前很多第三方庫內包含了各種語言包,但是大多數情況下我們的應用是不需要國際化,僅僅需要中文的就可以了,所以可以 resConfigs 配置來去掉多余的語言包:
android {
defaultConfig {
...
resConfigs "zh"
}
}
如上所示,打包時只會將中文的資源包打進去,這樣也可以一定程度上減小 apk 的體積。
6. ProGuard 中的壓縮優化配置
關于 ProGuard 的配置有很多,具體需要查看文檔才能知道作用是什么。剛開始接觸的時候,我以為 ProGuard 只是做代碼混淆而用的,其實它的作用不僅僅如此,有2個配置我們經常會忽略掉:
- -dontshrink
聲明不進行壓縮操作。 - -dontoptimize
不對 class 進行優化,默認是開啟優化的。由于優化會進行類合并、內聯等,使用熱修復的應用,建議關閉優化。
我發現很多第三方庫,例如友盟統計SDK,官方給出的 ProGuard 配置都需要加上該配置,也就是說不進行代碼優化、壓縮,往往初次接觸者不明所以的照搬了。但其實如果你的應用沒有采用熱更新方案之類的,在 ProGuard 里去掉這2個配置,你會發現這對減小 apk 的大小效果很顯著。
以我們自己的一個應用為例,ProGuard 里加上這2個配置打出來的 apk 包大小約為 25.5M,去掉這2個配置之后打出的包大小約為 24.1M。簡直不敢相信,就這么2個小小的配置,居然能讓包大小縮減 1.4M 左右,效果非常明顯。
但是,實操過程中,我們發現有些頁面的圖標丟失變成黑色了,這些因為 ProGuard 判定某些資源文件沒有被使用,將它轉換成了一個 1*1 的占位文件了。這需要我們手動在 res/raw/keep.xml 里配置,明確告訴 ProGuard 哪些資源文件是不需要混淆壓縮的,以為自己的一個配置為例:
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="safe"
tools:keep="@mipmap/emoji_*,@mipmap/jietiao_ic_module_*"
>
</resources>
7. 小結
采用 ProGuard 是減小 apk 大小非常有效的方法之一,但是使用過程中可能附帶很多問題:有可能打包不成功、有可能圖片資源問題丟失、有可能莫明其妙的閃退,這些都需要我們對 ProGuard 的配置有個基本的了解,遇到問題才能迎刃而解,用好了它相信會有很大的幫助。
系列文章
Android apk瘦身最佳實踐(一):去除R.class
Android apk瘦身最佳實踐(二):代碼混淆和資源壓縮
Android apk瘦身最佳實踐(三):資源混淆原理
Android apk瘦身最佳實踐(四):采用AndResGuard進行資源混淆
Android apk瘦身最佳實踐(五):圖片壓縮
Android apk瘦身最佳實踐(六):采用D8編譯器