Android apk瘦身最佳實踐(二):代碼混淆和資源壓縮

要盡可能減小 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 無法編譯

從中我們可以得到一些結論:

  1. 開啟代碼混淆和資源壓縮后,apk 大小減少了約0.5M,一個小工程都尚且如此,在一個比較龐大的工程中開啟這倆選項,壓縮的大小還是很可觀的。
  2. 開啟資源壓縮與不開啟相比,只減少了約2000多字節的大小,可見大頭還是靠代碼混淆,資源壓縮只能起到錦上添花的作用。
  3. 關閉代碼混淆開啟資源壓縮,你會發現無法編譯,Android Studio 會提示你這倆必須配對使用,要開啟資源壓縮必須得開啟代碼混淆。

4. 資源壓縮會保留文件名不保留內容

我們再做個測試,在應用的資源中放一張png圖片姑且命名為 test.png,放一個layout布局文件命名為test.xml,并且確保這2個資源文件沒有任何代碼會引用,我們開啟代碼混淆和資源壓縮打包生成apk文件。

生成apk文件后,直接將apk包拖到 Android Studio 中,可以查看apk包的相關信息,細心一點的話你會發現幾個有趣的現象:

  1. 在圖片資源文件夾中,仍然可以看到名為 test.png 的圖片,但是你打開看到的是一個 1*1 的圖片;
  2. 在 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編譯器

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。