簡介
随着項目的不斷疊代,代碼量跟資源檔案不斷增多。那麼就會出現打包後的 APK 檔案越來越大,如果突然有一天你們老闆或上司叫你優化 APK 大小,你還不知道怎麼優化那就有點說不過去了,這篇文章咱們就來一起分析并優化 APK 體積大小吧。
先上圖看下優化後的效果

分析 APK 資源占用
注意:
我是在 GitHub 找了一個人氣比較高的開源項目,需要的話自己可以點選下載下傳,自己動手嘗試一番.
分析工具直接用的 AS Build/Analyze APK
從上面圖中得出 assets > classes.dex > res > lib 其中資源檔案占用最大。
下面我們就來看看怎麼減小 APK 大小吧,
優化 APK 體積八大步
1. 将圖檔轉換為 webp 格式
Webp 概念
WebP 是一種同時提供了有損壓縮與無損壓縮的圖檔檔案格式,派生自視訊編碼格式 VP8。WebP 最初在2010年釋出,目标是減少檔案大小,但達到 和 JEPG 格式相同的圖檔品質,希望能夠減少圖檔檔在網絡上的發送時間。2011年11月8日,Google 開始讓 WebP 支援無損壓縮和透明色的功能。
根據 Google 較早的測試,WebP 的無損壓縮比網絡上找到的 PNG 檔少了 45% 的檔案大小,即使這些 PNG 檔在使用 PNGCRUSH 和 PNGOUT 處理過,WebP 還是可以減少 28% 的檔案大小。就目前而言,Webp 可以讓圖檔大小平均減少 70% 。WebP 是未來圖檔格式的發展趨勢。
PNG / JPG to Webp
點選圖檔或者檔案夾右鍵選擇 Convert to Webp 格式,将 png / jpg 圖檔壓縮為 webp 格式圖檔.
最後我們隻減少了不到 200 kb 左右,有可能項目圖檔資源本來就沒有多大,隻是太多小圖檔導緻的。
應用場景及優勢
- 用戶端軟體,内嵌了基于 Chromium 的 webview,這類浏覽器中應用的網頁是可以完全使用WebP 格式,提升加載渲染速度,不考慮相容。
- 用 node-webkit 開發的程式,用 WebP 可以減少檔案包的體積。
- 移動應用 或 網頁遊戲 ,界面需要大量圖檔,可以嵌入 WebP 的解碼包,能夠節省使用者流量,提升通路速度優勢:
- 對于 PNG 圖檔,WebP 比 PNG 小了45%。
2. 去除多語言
在 app/build.gradle 添加
android{
...
defaultConfig{
...
//隻保留英語
resConfigs "en"
}
}
複制
這裡我們發現減少了大概 200 kb
3. 去除不必要 so 庫
通過反編譯 Android 微信版本 得知,微信也隻适配了 armeabi-v7a 架構,那麼我們删掉其它庫的支援吧。
android{
...
defaultConfig{
...
ndk {
//設定支援的SO庫架構
abiFilters "armeabi-v7a"
}
}
}
複制
又優化了差不多 600 kb ,繼續。
4. 去除無用資源 Link 檢查(謹慎删除)
概念
Lint 是 Android Studio 提供的 代碼掃描分析工具,它可以幫助我們發現代碼結構 / 品質問題,同時提供一些解決方案,而且這個過程不需要我們手寫測試用例。代碼疊代版本一多,很容易會遺留一些無用的代碼、資源檔案,我們可以使用 Lint 進行清除。
怎麼使用 Link 檢查
打開 AS 工具,找到 Analyze > Run Inspection By Name > unused resources
優化
發現我們 link 大概優化了 700 kb繼續。
注意
因為 link 是檢查有沒有引用來做的判斷是否使用了資源,那麼如果是這種方式勒,是以在删除的時候一定要謹慎。
//動态擷取資源 id , 未直接使用 R.xx.xx ,則這個 id 代表的資源會被認為沒有使用過(類似不能混淆反射類)
int indetifier =getResources().getIdentifier("img_bubble_receive", "drawable", getPackageName()); getResources().getDrawable(indetifier);
複制
5. 開啟混淆
優化了大概 1.7M 繼續。
6.移除無用資源 shinkResource
-
開啟 shinkResource = true
buildTypes { release { minifyEnabled true shrinkResources = true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { shrinkResources = true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
這個有可能 link 删除了無用資源,是以沒有在優化了
7.開啟删除無用資源 (嚴格模式和普通模式) - 這個我這裡就不可測試,你們下來可以測試下效果
普通模式也就是自定義模式
如果您有想要保留或舍棄的特定資源,請在您的項目中建立一個包含
<resources>
标記的 XML 檔案,并在
tools:keep
屬性中指定每個要保留的資源,在
tools:discard
屬性中指定每個要舍棄的資源。這兩個屬性都接受逗号分隔的資源名稱清單。您可以使用星号字元作為通配符。
例如:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
tools:discard="@layout/unused2" />
複制
将該檔案儲存在項目資源中,例如,儲存在
res/raw/keep.xml
。建構不會将該檔案打包到 APK 之中。
指定要舍棄的資源可能看似愚蠢,因為您本可将它們删除,但在使用建構變體時,這樣做可能很有用。例如,如果您明知給定資源表面上會在代碼中使用(并是以不會被壓縮器移除),但實際不會用于給定建構變體,就可以将所有資源放入公用項目目錄,然後為每個建構變體建立一個不同的
keep.xml
檔案。建構工具也可能無法根據需要正确識别資源,這是因為編譯器會添加内聯資源 ID,而資源分析器可能不知道真正引用的資源和恰巧具有相同值的代碼中的整數值之間的差别。
嚴格模式
正常情況下,資源壓縮器可準确判定系統是否使用了資源。不過,如果您的代碼調用
Resources.getIdentifier()
(或您的任何庫進行了這一調用 - AppCompat 庫會執行該調用),這就表示您的代碼将根據動态生成的字元串查詢資源名稱。當您執行這一調用時,預設情況下資源壓縮器會采取防禦性行為,将所有具有比對名稱格式的資源标記為可能已使用,無法移除。
例如,以下代碼會使所有帶
img_
字首的資源标記為已使用。
String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());
複制
資源壓縮器還會浏覽代碼以及各種
res/raw/
資源中的所有字元串常量,尋找格式類似于
file:///android_res/drawable//ic_plus_anim_016.png
的資源網址。如果它找到與其類似的字元串,或找到其他看似可用來建構與其類似的網址的字元串,則不會将它們移除。
這些是預設情況下啟用的安全壓縮模式的示例。但您可以停用這一“有備無患”處理方式,并指定資源壓縮器隻保留其确定已使用的資源。要執行此操作,請在
keep.xml
檔案中将
shrinkMode
設定為
strict
,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict" />
複制
如果您确已啟用嚴格壓縮模式,并且代碼也引用了包含動态生成字元串的資源(如上所示),則必須利用
tools:keep
屬性手動保留這些資源。
8. AndResGuard 微信資源壓縮方案
什麼是 AndResGuard
AndResGuard 是一個縮小 APK 大小的工具,它的原理類似 Java Proguard ,但是隻針對資源。它會将原本冗長的資源路徑變短,例如将 res/drawable/wechat 變為 r/d/a。
為什麼使用 AndResGuard
在以往的開發中,我們通常隻混淆了代碼,資源檔案卻暴露在他人面前,res 檔案夾下所有檔案名的可讀性過強。
使用後的效果
AndResGuard 的配置
-
項目根目錄下 build.gradle 中,添加插件的依賴:
dependencies { classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.16' }
- 在 app 目錄下,建立 and_res_guard.gradle 檔案
apply plugin: 'AndResGuard'
andResGuard {
mappingFile = null
use7zip = true
useSign = true
keepRoot = false
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"resources.arsc"
]
whiteList = [
// your icon
"R.drawable.icon",
// for fabric
"R.string.com.crashlytics.*",
// for umeng update
"R.string.tb_*",
"R.layout.tb_*",
"R.drawable.tb_*",
"R.drawable.u1*",
"R.drawable.u2*",
"R.color.tb_*",
// umeng share for sina
"R.drawable.sina*",
// for google-services.json
"R.string.google_app_id",
"R.string.gcm_defaultSenderId",
"R.string.default_web_client_id",
"R.string.ga_trackingId",
"R.string.firebase_database_url",
"R.string.google_api_key",
"R.string.google_crash_reporting_api_key",
//友盟
"R.string.umeng*",
"R.string.UM*",
"R.layout.umeng*",
"R.drawable.umeng*",
"R.id.umeng*",
"R.anim.umeng*",
"R.color.umeng*",
"R.style.*UM*",
"R.style.umeng*",
//融雲
"R.drawable.u*",
"R.drawable.rc_*",
"R.string.rc_*",
"R.layout.rc_*",
"R.color.rc_*",
"R.id.rc_*",
"R.style.rc_*",
"R.dimen.rc_*",
"R.array.rc_*"
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.2.10'
}
}
複制
-
在 app 子產品下的 build.gradle 檔案添加
apply from: 'and_res_guard.gradle'
- 打包完之後效果圖
資源壓縮了大概 1M
總結
- 項目體積越大,資源越多,效果就越明顯。
- 使用 Link 删除資源的話,一定要謹慎,提前做好備份。
- 咱們這裡因為項目本身隻有 10 M 多,最後優化了 4.5 M 下去。也還是很不容易的。
好了,文章到這裡就結束了,如果你覺得文章寫得不錯就給個贊呗?如果你覺得那裡值得改進的,請給我留言。一定會認真查詢,修正不足。謝謝。
Android架構師之路很漫長,一起共勉吧!
以下牆裂推薦閱讀!!!
- Android學習筆記參考(敲黑闆!!)
- “寒冬未過”,阿裡P9架構分享Android必備技術點,讓你offer拿到手軟!
- 畢業3年,我是如何從年薪10W的拖拽工程師成為30W資深Android開發者!
- 騰訊T3大牛帶你了解 2019 Android開發趨勢及必備技術點!
- 八年Android開發,從碼農到架構師分享我的技術成長之路,共勉!
最後祝大家生活愉快~