本專欄專一分享大型Bat面試知識,後續會持續更新,喜歡的話麻煩點擊一個關注html
github面試↓專題連接
關於我
面試官: 類比於微信,如何對Apk進行極限壓縮,談下Android壓縮8大步
(面試官是很是注重性能優化的,考求職者是否具有APK壓縮技能)node
求職者:應該從每一步壓縮開始,壓縮的過程本質是擠牙膏的過程,一步一步擠。將Apk壓縮分爲8個步驟android
隨着項目的不斷迭代,代碼量跟資源文件不斷增多。那麼就會出現打包後的 APK 文件愈來愈大,若是忽然有一天大家老闆或領導叫你優化 APK 大小,你還不知道怎麼優化那就有點說不過去了,這篇文章我們就來一塊兒分析並優化 APK 體積大小吧。git
注意:
在 GitHub 找了一我的氣比較高的開源項目,須要的話本身能夠點擊下載,本身動手嘗試一番.
程序員
分析工具直接用的 AS Build/Analyze APKgithub
從上面圖中得出 assets > classes.dex > res > lib 其中資源文件佔用最大。web
下面咱們就來看看怎麼減少 APK 大小吧,面試
WebP 是一種同時提供了有損壓縮與無損壓縮的圖片文件格式,派生自視頻編碼格式 VP8。WebP 最初在2010年發佈,目標是減小文件大小,但達到 和 JEPG 格式相同的圖片質量,但願可以減小圖片檔在網絡上的發送時間。2011年11月8日,Google 開始讓 WebP 支持無損壓縮和透明色的功能。json
根據 Google 較早的測試,WebP 的無損壓縮比網絡上找到的 PNG 檔少了 45% 的文件大小,即便這些 PNG 檔在使用 PNGCRUSH 和 PNGOUT 處理過,WebP 仍是能夠減小 28% 的文件大小。就目前而言,Webp 可讓圖片大小平均減小 70% 。WebP 是將來圖片格式的發展趨勢。api
點擊圖片或者文件夾右鍵選擇 Convert to Webp 格式,將 png / jpg 圖片壓縮爲 webp 格式圖片
最後咱們只減小了不到 200 kb 左右,有可能項目圖片資源原本就沒有多大,只是太多小圖片致使的。
應用場景及優點
在 app/build.gradle 添加
android{ ... defaultConfig{ ... //只保留英語 resConfigs "en" } }
這裏咱們發現減小了大概 200 kb
經過反編譯 Android 微信版本 得知,微信也只適配了 armeabi-v7a 架構,那麼咱們刪掉其它庫的支持吧。
android{ ... defaultConfig{ ... ndk { //設置支持的SO庫架構 abiFilters "armeabi-v7a" } } }
又優化了差很少 600 kb ,繼續。
Lint 是 Android Studio 提供的 代碼掃描分析工具,它能夠幫助咱們發現代碼結構 / 質量問題,同時提供一些解決方案,並且這個過程不須要咱們手寫測試用例。代碼迭代版本一多,很容易會遺留一些無用的代碼、資源文件,咱們可使用 Lint 進行清除。
打開 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);
優化了大概 1.7M 繼續
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 刪除了無用資源,因此沒有在優化了
普通模式也就是自定義模式
若是您有想要保留或捨棄的特定資源,請在您的項目中建立一個包含 <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 屬性手動保留這些資源。
AndResGuard 是一個縮小 APK 大小的工具,它的原理相似 Java Proguard ,可是隻針對資源。它會將本來冗長的資源路徑變短,例如將 res/drawable/wechat 變爲 r/d/a。
在以往的開發中,咱們一般只混淆了代碼,資源文件卻暴露在他人面前,res 文件夾下全部文件名的可讀性過強。
AndResGuard 的配置
dependencies { classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.16' }
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' } }
apply from: 'and_res_guard.gradle'
資源壓縮了大概 1M
項目體積越大,資源越多,效果就越明顯。
使用 Link 刪除資源的話,必定要謹慎,提早作好備份。
我們這裏由於項目自己只有 22 M 多,最後優化了 4.5 M 下去。也仍是很不容易的。
github面試↓專題連接
關於我
領取Android高級面試專題
很是但願和你們一塊兒交流 , 共同進步
目前是一名程序員,不只分享 Android開發相關知識,同時還分享技術人成長曆程,包括我的總結,職場經驗,面試經驗等,但願能讓你少走一點彎路。