類比于微信,如何對Apk進(jìn)行極限壓縮,談下Android壓縮8大步
簡介
隨著項(xiàng)目的不斷迭代,代碼量跟資源文件不斷增多。那么就會(huì)出現(xiàn)打包后的 APK 文件越來越大,如果突然有一天你們老板或領(lǐng)導(dǎo)叫你優(yōu)化 APK 大小,你還不知道怎么優(yōu)化那就有點(diǎn)說不過去了,這篇文章咱們就來一起分析并優(yōu)化 APK 體積大小吧。
分析 APK 資源占用
注意:
我是在 GitHub 找了一個(gè)人氣比較高的開源項(xiàng)目,需要的話自己可以點(diǎn)擊下載,自己動(dòng)手嘗試一番。
從上面圖中得出 assets > classes.dex > res > lib 其中資源文件占用最大。
下面我們就來看看怎么減小 APK 大小吧。
優(yōu)化 APK 體積八大步
1. 將圖片轉(zhuǎn)換為 webp 格式
Webp 概念
WebP 是一種同時(shí)提供了有損壓縮與無損壓縮的圖片文件格式,派生自視頻編碼格式 VP8。WebP 最初在2010年發(fā)布,目標(biāo)是減少文件大小,但達(dá)到 和 JEPG 格式相同的圖片質(zhì)量,希望能夠減少圖片檔在網(wǎng)絡(luò)上的發(fā)送時(shí)間。2011年11月8日,Google 開始讓 WebP 支持無損壓縮和透明色的功能。
根據(jù) Google 較早的測試,WebP 的無損壓縮比網(wǎng)絡(luò)上找到的 PNG 檔少了 45% 的文件大小,即使這些 PNG 檔在使用 PNGCRUSH 和 PNGOUT 處理過,WebP 還是可以減少 28% 的文件大小。就目前而言,Webp 可以讓圖片大小平均減少 70% 。WebP 是未來圖片格式的發(fā)展趨勢。
PNG / JPG to Webp
點(diǎn)擊圖片或者文件夾右鍵選擇 Convert to Webp 格式,將 png / jpg 圖片壓縮為 webp 格式圖片。
最后我們只減少了不到 200 kb 左右,有可能項(xiàng)目圖片資源本來就沒有多大,只是太多小圖片導(dǎo)致的。
應(yīng)用場景及優(yōu)勢
- 客戶端軟件,內(nèi)嵌了基于 Chromium 的 webview,這類瀏覽器中應(yīng)用的網(wǎng)頁是可以完全使用WebP 格式,提升加載渲染速度,不考慮兼容。
- 用 node-webkit 開發(fā)的程序,用 WebP 可以減少文件包的體積。
- 移動(dòng)應(yīng)用 或 網(wǎng)頁游戲 ,界面需要大量圖片,可以嵌入 WebP 的解碼包,能夠節(jié)省用戶流量,提升訪問速度優(yōu)勢:
- 對于 PNG 圖片,WebP 比 PNG 小了45%。
2. 去除多語言
在 app/build.gradle 添加
- android{
- ...
- defaultConfig{
- ...
- //只保留英語
- resConfigs "en"
- }
- }
3. 去除不必要 so 庫
通過反編譯 Android 微信版本 得知,微信也只適配了 armeabi-v7a 架構(gòu),那么我們刪掉其它庫的支持吧。
- android{
- ...
- defaultConfig{
- ...
- ndk {
- //設(shè)置支持的SO庫架構(gòu)
- abiFilters "armeabi-v7a"
- }
- }
- }
4. 去除無用資源 Link 檢查(謹(jǐn)慎刪除)
概念
Lint 是 Android Studio 提供的 代碼掃描分析工具,它可以幫助我們發(fā)現(xiàn)代碼結(jié)構(gòu) / 質(zhì)量問題,同時(shí)提供一些解決方案,而且這個(gè)過程不需要我們手寫測試用例。代碼迭代版本一多,很容易會(huì)遺留一些無用的代碼、資源文件,我們可以使用 Lint 進(jìn)行清除。
怎么使用 Link 檢查
打開 AS 工具,找到 Analyze > Run Inspection By Name > unused resources
優(yōu)化
發(fā)現(xiàn)我們 link 大概優(yōu)化了 700 kb繼續(xù)。
注意
因?yàn)?link 是檢查有沒有引用來做的判斷是否使用了資源,那么如果是這種方式勒,所以在刪除的時(shí)候一定要謹(jǐn)慎。
- //動(dòng)態(tài)獲取資源 id , 未直接使用 R.xx.xx ,則這個(gè) id 代表的資源會(huì)被認(rèn)為沒有使用過(類似不能混淆反射類)
- int indetifier =getResources().getIdentifier("img_bubble_receive", "drawable", getPackageName()); getResources().getDrawable(indetifier);
5. 開啟混淆
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'
- }
- }
這個(gè)有可能 link 刪除了無用資源,所以沒有在優(yōu)化了。
7.開啟刪除無用資源 (嚴(yán)格模式和普通模式) - 這個(gè)我這里就不可測試,你們下來可以測試下效果。
普通模式也就是自定義模式
如果您有想要保留或舍棄的特定資源,請?jiān)谀捻?xiàng)目中創(chuàng)建一個(gè)包含 標(biāo)記的 XML 文件,并在 tools:keep 屬性中指定每個(gè)要保留的資源,在 tools:discard 屬性中指定每個(gè)要舍棄的資源。這兩個(gè)屬性都接受逗號(hào)分隔的資源名稱列表。您可以使用星號(hào)字符作為通配符。
例如:
- <?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" />
將該文件保存在項(xiàng)目資源中,例如,保存在 res/raw/keep.xml。構(gòu)建不會(huì)將該文件打包到 APK 之中。
指定要舍棄的資源可能看似愚蠢,因?yàn)槟究蓪⑺鼈儎h除,但在使用構(gòu)建變體時(shí),這樣做可能很有用。例如,如果您明知給定資源表面上會(huì)在代碼中使用(并因此不會(huì)被壓縮器移除),但實(shí)際不會(huì)用于給定構(gòu)建變體,就可以將所有資源放入公用項(xiàng)目目錄,然后為每個(gè)構(gòu)建變體創(chuàng)建一個(gè)不同的 keep.xml 文件。構(gòu)建工具也可能無法根據(jù)需要正確識(shí)別資源,這是因?yàn)榫幾g器會(huì)添加內(nèi)聯(lián)資源 ID,而資源分析器可能不知道真正引用的資源和恰巧具有相同值的代碼中的整數(shù)值之間的差別。
嚴(yán)格模式
正常情況下,資源壓縮器可準(zhǔn)確判定系統(tǒng)是否使用了資源。不過,如果您的代碼調(diào)用 Resources.getIdentifier()(或您的任何庫進(jìn)行了這一調(diào)用 - AppCompat 庫會(huì)執(zhí)行該調(diào)用),這就表示您的代碼將根據(jù)動(dòng)態(tài)生成的字符串查詢資源名稱。當(dāng)您執(zhí)行這一調(diào)用時(shí),默認(rèn)情況下資源壓縮器會(huì)采取防御性行為,將所有具有匹配名稱格式的資源標(biāo)記為可能已使用,無法移除。
例如,以下代碼會(huì)使所有帶 img_ 前綴的資源標(biāo)記為已使用。
- String name = String.format("img_%1d", angle + 1);
- res = getResources().getIdentifier(name, "drawable", getPackageName());
資源壓縮器還會(huì)瀏覽代碼以及各種 res/raw/ 資源中的所有字符串常量,尋找格式類似于 file:///android_res/drawable//ic_plus_anim_016.png 的資源網(wǎng)址。如果它找到與其類似的字符串,或找到其他看似可用來構(gòu)建與其類似的網(wǎng)址的字符串,則不會(huì)將它們移除。
這些是默認(rèn)情況下啟用的安全壓縮模式的示例。但您可以停用這一“有備無患”處理方式,并指定資源壓縮器只保留其確定已使用的資源。要執(zhí)行此操作,請?jiān)? keep.xml 文件中將 shrinkMode 設(shè)置為 strict,如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <resources xmlns:tools="http://schemas.android.com/tools"
- tools:shrinkMode="strict" />
如果您確已啟用嚴(yán)格壓縮模式,并且代碼也引用了包含動(dòng)態(tài)生成字符串的資源(如上所示),則必須利用 tools:keep 屬性手動(dòng)保留這些資源。
8. AndResGuard 微信資源壓縮方案
什么是 AndResGuard
AndResGuard 是一個(gè)縮小 APK 大小的工具,它的原理類似 Java Proguard ,但是只針對資源。它會(huì)將原本冗長的資源路徑變短,例如將 res/drawable/wechat 變?yōu)?r/d/a。
為什么使用 AndResGuard
在以往的開發(fā)中,我們通常只混淆了代碼,資源文件卻暴露在他人面前,res 文件夾下所有文件名的可讀性過強(qiáng)。
使用后的效果
AndResGuard 的配置
項(xiàng)目根目錄下 build.gradle 中,添加插件的依賴:
- dependencies {
- classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.16'
- }
在 app 目錄下,創(chuàng)建 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'
打包完之后效果圖
總結(jié)
項(xiàng)目體積越大,資源越多,效果就越明顯。
使用 Link 刪除資源的話,一定要謹(jǐn)慎,提前做好備份。
咱們這里因?yàn)轫?xiàng)目本身只有 22 M 多,最后優(yōu)化了 4.5 M 下去。也還是很不容易的。