<b> apk瘦身記,如何實作高達53%的壓縮效果</b>
<b></b>
<b>作者:非戈@阿裡聚安全</b>
apk是android系統安裝包的檔案格式,關于這個話題其實是一個老生常談的題目,不論是公司内部,還是外部網絡,前人前輩已經總結出很多方法和規律。不過随着移動端技術近兩年的飛速發展,一些新的思維方式和優化方法也逐漸湧現和成熟起來。筆者在實踐過程中踩過一些坑,收獲了一些經驗,在這裡做個思考和總結,是以随筆給大家,希望對大家從事相關工作的時候有所幫助和參考,同時也是抛磚引玉,希望大家共同探讨這個開放性的話題。
關于為什麼apk要瘦身,這個不多說,隻從三個方面唠叨一下,對于使用者(或者客戶)來說,apk越大,在下載下傳安裝過程中,他們耗費的流量會越多,安裝等待時間也會越長;對于産品本身,意味着下載下傳轉化率會越低(因為競品中,使用者有更多機會選擇那個體驗最好,功能最多,性能最好,包最小的);對于研發來說,是一種優化改進技術的機會。
欲瘦身,我們先找找胖的原因和問題。按目标-路徑-資源的思維模式,找原因和問題有如下幾條路徑,一是拍腦袋,按自己的經驗和判斷,甚至是主觀想象;二是去搜尋引擎找關鍵字,逛各種技術論壇聽技術大牛們怎麼說,看各類技術文章抽取提煉;三是用一種可測量的工具或者方法發現問題。
前兩種不贅述,我這裡說說第三種方法。用一種可測量的工具或者方法來分析,所謂工欲善其事,必先利其器。這個器可以可以自己鍛造,也可以用現成的。這裡推薦一個線上apk分析工具,因為是外部工具,是以大家請在使用過程中,不要上傳未釋出出去的産品,為了資料安全,筆者這裡拿一個github上開源的android項目作為瘦身示例。
如果想使用分析功能分析自己的産品,請登入并上傳自己産品的apk包,所有功能目前均免費使用,如果是想分析google play上已經釋出的産品,可以直接點選"play apps"檢視,還可以使用搜尋功能根據應用名和包名檢視結果。再次強調下,請不要上傳任何未釋出的産品。
<b>登入</b>
<b>上傳apk檔案</b>
分析結果摘要,可以看到一些概覽的資訊,apk檔案大小,總的方法數
檔案大小分析詳情頁,大檔案清單,這裡列出的是apk檔案中超過100k的檔案排行,這裡的檔案大小指的是apk檔案中的大小
各種知名sdk的大小以及占代碼整體的比例,這裡目前能識别出android support,jackson json parser, google play services, paypal, glide, okhttp, facebook sdk, fabric, gson等等,application表示app中自己編寫的代碼部分
各種類型檔案的大小以及排行
各種知名sdk占所有dex中方法數的比例
各種知名sdk的方法數排行榜
看完這個apk内剖圖是不是有一種神清氣爽的感覺!我把這個分析工具比做我們家買的智能體重秤,可以稱體重,脂肪含量,骨重,骨密度,肌肉含量等等,那麼,我們是不是發現了一些問題,進而把這些問題和我們之前靠經驗和一拍腦袋的原因可以用邏輯聯系在一起。
那麼,我們接下來可以通過分析資料整理出我們的優化目标
1. 大檔案排行榜裡,有11張png檔案的大小超過了100k,記住,這可是壓縮之後的啊;
2. 大檔案排行榜裡,resources.arsc的大小接近2m,這也是一個優化點;
3. 大檔案排行榜裡,classes.dex接近3m,classes.dex是代碼的載體,這塊的優化需要細分,再去看看細分sdk的排行榜;
4. 元件占比環圖裡,android support, jackson json parser和google play services是三方庫的前三甲;
5. 檔案類型排行榜裡,png, dex 和arsc是前三甲;
是以我們的目标是沒有蛀牙,不對,是下面的目标:
1. png圖檔優化;
2. resources.arsc檔案的優化;
3. 代碼優化
首先是第一個目标,圖檔的優化,慢點,我們看看這些圖為什麼這麼大先,準确的說,為什麼這些圖在apk(其實就是zip檔案)裡這麼大,好了,上工具分析。
這次用了一些簡單的工具組合,系統自帶的cmd就好。
指令執行的結果如下
通俗的說,當檔案是stored的方式存儲到zip,表示這個檔案并沒有經過壓縮,如果是defl:n的方式,表示通過deflated normal的方式壓縮存儲到zip。
<b>優化前:</b>
10536027位元組
<b>優化後:</b>
普通zip壓縮: 8786265位元組 (壓縮了将近17%)
采用7zip壓縮:8567150位元組 (壓縮了将近19%)
再看看這個工具做了什麼,對比下開啟資源混淆前後
<b>優化前</b>
<b>優化後</b>
1. 資源(png, xml, jpg等)名稱混淆,資源路徑名稱混淆以及名稱長度壓縮;
2. 原來以stored形式存儲到zip中的png檔案被改成了deflated(普通壓縮存儲)方式;
3. 意外發現resources.arsc, meta-inf/*.sf 以及 meta-inf/*.mf變小了,而且是解壓之後的檔案大小也變小了。
原來apk中資源(png, xml,以及properties檔案)的相對路徑會存放到meta-inf/*.sf 以及 meta-inf/*.mf中并為每個資源檔案計算sha1值并存儲在這兩個檔案中,至于為啥這麼做以及這兩個sha1有啥差別和作用請參考網絡上關于這方面知識的文章,已超出本文的主題是以這裡不再贅述。
對于resources.arsc檔案
很容易看出來它是資源檔案索引表,是以,看到這裡大家應該明白這三個檔案為啥會變小了吧。
順着resources.arsc往下看,發現一個有趣的東西:
這又将成為一個優化點,去除那些沒用的翻譯資源,引入一些第三方的sdk,往往這些sdk帶了很多翻譯資源在裡面,比如android support庫,去掉後我們來看看效果。
假設我們隻保留英文,當然隻是個實驗,現實中看具體情況了,
采用7zip壓縮:8220738位元組 (壓縮了将近22%,再增加3個點)
當然,真實的項目裡不可能這樣,但是蚊子肉也是肉啊!
其實,我想說的是這提供了一種優化思路,就是利用gradle的配置幹掉無用的資源,同樣的可以用在so本地庫上,分辨率(gradle配置已deprecated)上。
gradle配置示例如下:
記得包在android{}中間哦。那麼,有人要問了,abi裡腫麼沒有x86?據說intel提供了一個解決方案叫houdini,是一個運作在x86裝置上的中間件,可以将arm轉碼為x86的指令,不過效率很低,有些運算型的,比如計算md5和sha1,甚至不如java,筆者曾經做過測試對比,又是另外一個話題,此處不贅述,感興趣的讀者可以移步。
到此為止,我們已經在朝第一個目标邁進,不經意間發現了第一個目标和第二個目标之間的關系,是以利用資源混淆工具,達成了第二個目标。
利用7zip壓縮,我們對整個包進行了2個點的壓縮,這是一個超出預期的成果。
關于第一個目标,我們的路徑還沒有結束,拍腦袋想出來的路徑是壓縮png,非alpha圖轉成jpg,還有什麼?是以去各種技術論壇逛了一圈,請教了各種技術大牛,梳理的路徑如下:
1. 手動lint檢查,手動删除代碼中沒有引用到的資源,實際效果不等。
在android studio中打開“analyze” 然後選擇"inspect code...",範圍選擇整個項目,然後點選"ok"
配置如下圖
2. gradle腳本中開啟shrinkresources
腳本參考如下
采用7zip壓縮:8115283位元組 (壓縮了将近23%,再增加1個點)
3. 使用圖檔壓縮工具,壓縮png圖的大小,将非alpha的圖轉換成jpg形式,關于這點同僚以及網絡上的大牛們已經整理的很詳細了,我這裡做簡單總結,欲知詳情,請見附錄的參考。
使用tinypng,我隻想說咱們在公司做産品,此方案慎用,上傳任何未釋出産品的内容到外部網絡,都有可能引起資料洩漏,是以慎用此方案。下面說替代方案。
<a href="http://wasted.werk01.de/" target="_blank">wasted</a>
<a href="http://pngquant.org/" target="_blank">pngquant</a>
<a href="https://pngmini.com/" target="_blank">imagealpha</a>
<a href="https://imageoptim.com/" target="_blank">imageoptim</a>
png轉成jpg格式 具體效果不等。
先上效果圖:
采用7zip壓縮:4926912位元組 (壓縮了将近53%,再增加30個點)
沒看錯吧,是30個點,目前apk的大小是原始apk大小的一半不到,而我做的,一行代碼木有改動,僅用了一些工具而已!
說人話,我木有吃減肥藥,木有絕食,體重卻輕了一半!!!
但是,目前卻沒能用到項目中,因為有兩個坑
在三星的部分機型上,部分有alpha背景部分的圖會有一條很明顯的黑線,這裡就不上圖了,這個問題目前通過白名單的方式不去做成webp的圖來處理;
在小米2刷成4.xx的手機上,未能正确識别xml檔案中描述的webp圖檔,導緻界面起來後加載xml布局檔案,檔案加載webp失敗,報錯說resource file not found,導緻app發生崩潰。跟蹤發現是小米機器代理了類resource為miuiresource,但是這個miuiresource未能正确識别webp,是以導緻加載資源檔案失敗,初步判定,目前暫時沒有解決方案,是以隻能忍痛放棄這個優化方案。
關于第一個目标,圖檔資源的優化,就寫到這裡了。
第二個目标已達成,剩下第三個目标,代碼的優化,梳理如下優化路徑:
1. 開啟proguard的代碼優化
将proguardfiles getdefaultproguardfile('proguard-android.txt'),'proguard-project.txt'
改為proguardfiles getdefaultproguardfile('proguard-android-optimize.txt'),'proguard-project.txt'
開啟代碼優化後的注意點請參見附錄。
2. 去除無用的庫
如果apk支援的最低版本是api14,而代碼中沒有用到高于api14的api就可以考慮拿掉整個android support庫。
3. 用更小的庫替代方案
如果隻用到了谷歌統計,那麼就不要把整個google play services都內建進來,隻內建需要的部分。
4. 定期清理廢棄的代碼
定期删除無用的邏輯和過期的業務功能子產品,以及廢棄的a/b test代碼。
5. 業務子產品采用插件化架構,代碼動态從雲端拉取
插件化,這是另外一個課題了,這裡不贅述。
apk瘦身記最終的成果
10536027位元組壓縮到4926912位元組, 壓縮了将近53%
1. 腳本中開啟資源混淆和資源壓縮
2. 用7zip代替zip
3. gradle腳本中開啟代碼混淆優化和無用資源删除
4. 用更小的圖,使用壓縮工具壓縮圖檔大小
5. 去除無用的資源,語言,本地so庫,二方三方庫和分辨率
6. 用更小的庫
7. 嘗試将android support庫徹底踢出你的項目
8. 定期清理代碼
9. 嘗試用h5編寫界面,圖檔雲端擷取
10. 嘗試插件化業務子產品
11. 尋找到zip檔案夾中所有用store形式存儲的檔案(不限于raw目錄下),嘗試壓縮,以及替代方案加載這些資源
12. 嘗試webp的圖檔加載方案,尋求突破
最後,繼續學習和嘗試新的優化方案
以此文獻給“唯瘦身與産品不可辜負”的技術們!!!
阿裡聚安全由阿裡巴巴移動安全部出品,面向企業和開發者提供企業安全解決方案,全面覆寫移動安全、資料風控、内容安全、實人認證等次元,并在業界率先提出“以業務為中心的安全”,賦能生态,與行業共享阿裡巴巴集團多年沉澱的專業安全能力。