APK瘦身记,怎样实现高达53%的压缩效果
作者:非戈@阿里移動(dòng)安全,很多其它技術(shù)干貨。請(qǐng)?jiān)L問阿里聚安全博客
1、我是怎么思考這件事情的
APK是Android系統(tǒng)安裝包的文件格式。關(guān)于這個(gè)話題事實(shí)上是一個(gè)老生常談的題目。不論是公司內(nèi)部。還是外部網(wǎng)絡(luò),前人前輩已經(jīng)總結(jié)出非常多方法和規(guī)律。
只是隨著移動(dòng)端技術(shù)近兩年的飛速發(fā)展,一些新的思維方式和優(yōu)化方法也逐漸涌現(xiàn)和成熟起來。
筆者在實(shí)踐過程中踩過一些坑,收獲了一些經(jīng)驗(yàn)。在這里做個(gè)思考和總結(jié),所以隨筆給大家,希望對(duì)大家從事相關(guān)工作的時(shí)候有所幫助和參考,同一時(shí)候也是拋磚引玉。希望大家共同探討這個(gè)開放性的話題。
關(guān)于為什么APK要瘦身。這個(gè)不多說,僅僅從三個(gè)方面嘮叨一下。對(duì)于用戶(或者客戶)來說。APK越大,在下載安裝過程中。他們耗費(fèi)的流量會(huì)越多,安裝等待時(shí)間也會(huì)越長。對(duì)于產(chǎn)品本身,意味著下載轉(zhuǎn)化率會(huì)越低(由于競(jìng)品中。用戶有很多其它機(jī)會(huì)選擇那個(gè)體驗(yàn)最好。功能最多,性能最好。包最小的)。對(duì)于研發(fā)來說,是一種優(yōu)化改進(jìn)技術(shù)的機(jī)會(huì)。
欲瘦身,我們先找找胖的原因和問題。
按目標(biāo)-路徑-資源的思維模式,找原因和問題有例如以下幾條路徑,一是拍腦袋,按自己的經(jīng)驗(yàn)和推斷,甚至是主觀想象;二是去搜索引擎找keyword,逛各種技術(shù)論壇聽技術(shù)大牛們?cè)趺凑f,看各類技術(shù)文章抽取提煉。三是用一種可測(cè)量的工具或者方法發(fā)現(xiàn)問題。
前兩種不贅述。我這里說說第三種方法。用一種可測(cè)量的工具或者方法來分析,所謂工欲善其事,必先利其器。
這個(gè)器能夠能夠自己鍛造,也能夠用現(xiàn)成的。
這里推薦一個(gè)在線apk分析工具。由于是外部工具。所以大家請(qǐng)?jiān)谑褂眠^程中。不要上傳未公布出去的產(chǎn)品,為了數(shù)據(jù)安全,筆者這里拿一個(gè)github上開源的Android項(xiàng)目作為瘦身演示樣例。
2、尋找問題
NimbleDroid?是美國哥倫比亞大學(xué)的博士創(chuàng)業(yè)團(tuán)隊(duì)研發(fā)出來的分析Android app性能指標(biāo)的系統(tǒng),分析的方式有靜態(tài)和動(dòng)態(tài)兩種方式,當(dāng)中靜態(tài)分析能夠分析出APK安裝包中大文件排行榜。各種知名SDK的大小以及占代碼總體的比例。各種類型文件的大小以及占排行,各種知名SDK的方法數(shù)以及占全部dex中方法數(shù)的比例。廢話不多說,以下上高清無碼大圖看看顏值吧。
假設(shè)想使用分析功能分析自己的產(chǎn)品,請(qǐng)登錄并上傳自己產(chǎn)品的apk包,全部功能眼下均免費(fèi)使用,假設(shè)是想分析Google Play上已經(jīng)公布的產(chǎn)品。能夠直接點(diǎn)擊"Play Apps"查看,還能夠使用搜索功能依據(jù)應(yīng)用名和包名查看結(jié)果。再次強(qiáng)調(diào)下,請(qǐng)不要上傳不論什么未公布的產(chǎn)品。
登錄
上傳apk文件
分析結(jié)果摘要,能夠看到一些概覽的信息,apk文件大小,總的方法數(shù)
文件大小分析詳情頁,大文件列表,這里列出的是apk文件里超過100k的文件排行,這里的文件大小指的是apk文件里的大小
各種知名SDK的大小以及占代碼總體的比例,這里眼下能識(shí)別出Android Support,Jackson JSON parser, Google Play Services, Paypal, Glide, OkHttp, Facebook SDK, Fabric, Gson等等,Application表示App中自己編寫的代碼部分
各種類型文件的大小以及排行
各種知名SDK占全部dex中方法數(shù)的比例
各種知名SDK的方法數(shù)排行榜
看完這個(gè)apk內(nèi)剖圖是不是有一種神清氣爽的感覺!我把這個(gè)分析工具比做我們家買的智能體重秤,能夠稱體重,脂肪含量,骨重,骨密度,肌肉含量等等,那么。我們是不是發(fā)現(xiàn)了一些問題,進(jìn)而把這些問題和我們之前靠經(jīng)驗(yàn)和一拍腦袋的原因能夠用邏輯聯(lián)系在一起。
那么。我們接下來能夠通過分析數(shù)據(jù)整理出我們的優(yōu)化目標(biāo)
1.?????大文件排行榜里,有11張png文件的大小超過了100k,記住,這但是壓縮之后的啊;
2.?????大文件排行榜里。resources.arsc的大小接近2M,這也是一個(gè)優(yōu)化點(diǎn);
3.?????大文件排行榜里,classes.dex接近3M,classes.dex是代碼的載體。這塊的優(yōu)化須要細(xì)分,再去看看細(xì)分SDK的排行榜;
4.?????組件占比環(huán)圖里,Android Support, Jackson JSON Parser和Google Play Services是三方庫的前三甲。
5.?????文件類型排行榜里。png, dex?和arsc是前三甲;
3、梳理優(yōu)化目標(biāo)
所以我們的目標(biāo)是沒有蛀牙,不正確,是以下的目標(biāo):
1. png圖片優(yōu)化。
2. resources.arsc文件的優(yōu)化;
3.?代碼優(yōu)化
3.1圖片優(yōu)化的嘗試
首先是第一個(gè)目標(biāo),圖片的優(yōu)化,慢點(diǎn),我們看看這些圖為什么這么大先,準(zhǔn)確的說,為什么這些圖在apk(事實(shí)上就是zip文件)里這么大,好了,上工具分析。
這次用了一些簡(jiǎn)單的工具組合。系統(tǒng)自帶的cmd就好。
命令運(yùn)行的結(jié)果例如以下
恩。全部的png文件竟然是STORE的方式存儲(chǔ)到apk里的。關(guān)于zip里的STORE和DEFLATE,詳見?)
通俗的說,當(dāng)文件是STORED的方式存儲(chǔ)到zip。表示這個(gè)文件并沒有經(jīng)過壓縮,假設(shè)是Defl:N的方式,表示通過DEFLATED normal的方式壓縮存儲(chǔ)到zip。
這看起來有點(diǎn)不合理,png原封不動(dòng)的放入zip,當(dāng)然最后產(chǎn)出的apk會(huì)比較大。那么。怎樣解決呢?筆者首先嘗使用android gradle plugin的方式,發(fā)現(xiàn)aaptOptions和packagingOptions都未能解決這個(gè)問題。在github上發(fā)現(xiàn)一個(gè)開源項(xiàng)目AndResGuard。試了集成到項(xiàng)目中,再看結(jié)果例如以下:
優(yōu)化前:
10536027字節(jié)
優(yōu)化后:
普通zip壓縮:?8786265字節(jié) (壓縮了將近17%)
採用7zip壓縮:8567150字節(jié) (壓縮了將近19%)
再看看這個(gè)工具做了什么,對(duì)照下開啟資源混淆前后
優(yōu)化前
優(yōu)化后
1.?????資源(png, xml, jpg等)名稱混淆,資源路徑名稱混淆以及名稱長度壓縮;
2.?????原來以STORED形式存儲(chǔ)到zip中的png文件被改成了DEFLATED(普通壓縮存儲(chǔ))方式;
3.?????意外發(fā)現(xiàn)resources.arsc, META-INF/*.SF?以及?META-INF/*.MF變小了,并且是解壓之后的文件大小也變小了。
用apk反編譯神器jadx內(nèi)窺apk尋找真相
原來apk中資源(png, xml。以及properties文件)的相對(duì)路徑會(huì)存放到META-INF/*.SF?以及META-INF/*.MF中并為每一個(gè)資源文件計(jì)算SHA1值并存儲(chǔ)在這兩個(gè)文件里,至于為啥這么做以及這兩個(gè)SHA1有啥差別和作用請(qǐng)參考網(wǎng)絡(luò)上關(guān)于這方面知識(shí)的文章,已超出本文的主題所以這里不再贅述。
對(duì)于resources.arsc文件
非常easy看出來它是資源文件索引表。所以,看到這里大家應(yīng)該明確這三個(gè)文件為啥會(huì)變小了吧。
3.2一次意外的發(fā)現(xiàn)
順著resources.arsc往下看,發(fā)現(xiàn)一個(gè)有趣的東西,
這又將成為一個(gè)優(yōu)化點(diǎn),去除那些無用的翻譯資源。引入一些第三方的SDK,往往這些SDK帶了非常多翻譯資源在里面。比方android support庫。去掉后我們來看看效果。
如果我們僅僅保留英文。當(dāng)然僅僅是個(gè)實(shí)驗(yàn)。現(xiàn)實(shí)中看詳細(xì)情況了,
採用7zip壓縮:8220738字節(jié) (壓縮了將近22%,再添加3個(gè)點(diǎn))
當(dāng)然,真實(shí)的項(xiàng)目里不可能這樣。可是蚊子肉也是肉啊!
事實(shí)上,我想說的是這提供了一種優(yōu)化思路,就是利用gradle的配置干掉沒用的資源。相同的能夠用在so本地庫上,分辨率(gradle配置已deprecated)上。
gradle配置示比例如以下:
記得包在android{}中間哦。那么。有人要問了,abi里腫么沒有x86?據(jù)說intel提供了一個(gè)解決方式叫houdini。是一個(gè)執(zhí)行在x86設(shè)備上的中間件。能夠?qū)rm轉(zhuǎn)碼為x86的指令。只是效率非常低。有些運(yùn)算型的,比方計(jì)算MD5和SHA1,甚至不如java。筆者以前做過測(cè)試對(duì)照,又是另外一個(gè)話題。此處不贅述,感興趣的讀者能夠移步。
到此為止,我們已經(jīng)在朝第一個(gè)目標(biāo)邁進(jìn),不經(jīng)意間發(fā)現(xiàn)了第一個(gè)目標(biāo)和第二個(gè)目標(biāo)之間的關(guān)系。所以利用資源混淆工具,達(dá)成了第二個(gè)目標(biāo)。
利用7zip壓縮,我們對(duì)整個(gè)包進(jìn)行了2個(gè)點(diǎn)的壓縮,這是一個(gè)超出預(yù)期的成果。
3.3圖片優(yōu)化的方法
關(guān)于第一個(gè)目標(biāo)。我們的路徑還沒有結(jié)束,拍腦袋想出來的路徑是壓縮png,非alpha圖轉(zhuǎn)成jpg。還有什么?所以去各種技術(shù)論壇逛了一圈,請(qǐng)教了各種技術(shù)大牛,梳理的路徑例如以下:
1.?????手動(dòng)lint檢查。手動(dòng)刪除代碼中沒有引用到的資源,實(shí)際效果不等。
在Android Studio中打開“Analyze”?然后選擇"Inspect Code..."。范圍選擇整個(gè)項(xiàng)目,然后點(diǎn)擊"OK"
配置例如以下圖
2.?????gradle腳本中開啟shrinkResources
腳本參考例如以下
shrinkResources配合minifyEnabled使用效果更佳,詳見shrinkResources使用方法以及注意
採用7zip壓縮:8115283字節(jié) (壓縮了將近23%。再添加1個(gè)點(diǎn))
3.?????使用圖片壓縮工具。壓縮png圖的大小,將非alpha的圖轉(zhuǎn)換成jpg形式,關(guān)于這點(diǎn)同事以及網(wǎng)絡(luò)上的大牛們已經(jīng)整理的非常具體了。我這里做簡(jiǎn)單總結(jié),欲知詳情。請(qǐng)見附錄的參考。
- 使用tinypng,我僅僅想說咱們?cè)诠咀霎a(chǎn)品,此方案慎用。上傳不論什么未公布產(chǎn)品的內(nèi)容到外部網(wǎng)絡(luò),都有可能引起數(shù)據(jù)泄漏。所以慎用此方案。
以下說替代方案。
- WASTED
- pngquant
- ImageAlpha
- ImageOptim
- 以上工具太散,有沒有集成化的工具,答案是“有”,@心倫 童鞋開發(fā)的imagemin
- @姐夫童鞋開發(fā)的MSImageResourcesHelper
- png轉(zhuǎn)成jpg格式 詳細(xì)效果不等。
4.?????終極大殺器。png轉(zhuǎn)成webp,關(guān)于webp,很多其它詳情請(qǐng)參考谷歌官方文檔以及安卓開發(fā)人員在線參考
先上效果圖:
採用7zip壓縮:4926912字節(jié) (壓縮了將近53%,再添加30個(gè)點(diǎn))
沒看錯(cuò)吧,是30個(gè)點(diǎn),眼下apk的大小是原始apk大小的一半不到,而我做的,一行代碼木有修改。僅用了一些工具而已!
說人話,我木有吃減肥藥。木有絕食,體重卻輕了一半。。!
可是,眼下卻沒能用到項(xiàng)目中,由于有兩個(gè)坑
- 在三星的部分機(jī)型上。部分有alpha背景部分的圖會(huì)有一條非常明顯的黑線,這里就不上圖了,這個(gè)問題眼下通過白名單的方式不去做成webp的圖來處理。
- 在小米2刷成4.xx的手機(jī)上,未能正確識(shí)別xml文件里描寫敘述的webp圖片。導(dǎo)致界面起來后載入xml布局文件,文件載入webp失敗,報(bào)錯(cuò)說resource file not found,導(dǎo)致app發(fā)生崩潰。跟蹤發(fā)現(xiàn)是小米機(jī)器代理了類Resource為MIUIResource,可是這個(gè)MIUIResource未能正確識(shí)別webp,所以導(dǎo)致載入資源文件失敗,初步判定,眼下臨時(shí)沒有解決方式。所以僅僅能忍痛放棄這個(gè)優(yōu)化方案。
關(guān)于第一個(gè)目標(biāo)。圖片資源的優(yōu)化,就寫到這里了。
3.4代碼優(yōu)化
第二個(gè)目標(biāo)已達(dá)成,剩下第三個(gè)目標(biāo)。代碼的優(yōu)化,梳理例如以下優(yōu)化路徑:
1.?????開啟proguard的代碼優(yōu)化
將proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-project.txt'
改為proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-project.txt'
開啟代碼優(yōu)化后的注意點(diǎn)請(qǐng)參見附錄。
2.?????去除沒用的庫
假設(shè)apk支持的最低版本號(hào)是API14,而代碼中沒實(shí)用到高于api14的api就能夠考慮拿掉整個(gè)android support庫。
3.?????用更小的庫替代方案
假設(shè)僅僅用到了谷歌統(tǒng)計(jì)。那么就不要把整個(gè)google play services都集成進(jìn)來。僅僅集成須要的部分。
4.?????定期清理廢棄的代碼
定期刪除沒用的邏輯和過期的業(yè)務(wù)功能模塊,以及廢棄的A/B test代碼。
5.?????業(yè)務(wù)模塊採用插件化框架,代碼動(dòng)態(tài)從云端拉取
插件化,這是另外一個(gè)課題了,這里不贅述。
apk瘦身記終于的成果
10536027字節(jié)壓縮到4926912字節(jié),?壓縮了將近53%
總結(jié)
1.?????腳本中開啟資源混淆和資源壓縮
2.?????用7zip取代zip
3.?????gradle腳本中開啟代碼混淆優(yōu)化和無用資源刪除
4.?????用更小的圖。使用壓縮工具壓縮圖片大小
5.?????去除沒用的資源,語言,本地so庫,二方三方庫和分辨率
6.?????用更小的庫
7.?????嘗試將android support庫徹底踢出你的項(xiàng)目
8.?????定期清理代碼
9.?????嘗試用H5編寫界面,圖片云端獲取
10.??嘗試插件化業(yè)務(wù)模塊
11.??尋找到zip文件夾中全部用STORE形式存儲(chǔ)的文件(不限于raw文件夾下)。嘗試壓縮,以及替代方案載入這些資源
12.??嘗試webp的圖片載入方案。尋求突破
最后,繼續(xù)學(xué)習(xí)和嘗試新的優(yōu)化方案
以此文獻(xiàn)給“唯瘦身與產(chǎn)品不可辜負(fù)”的技術(shù)們!
。!
附錄
怎樣做到將apk大小降低6M
Android APP終極瘦身指南
APK瘦身實(shí)踐
作者:非戈@阿里移動(dòng)安全。很多其它技術(shù)干貨,請(qǐng)?jiān)L問阿里聚安全博客
總結(jié)
以上是生活随笔為你收集整理的APK瘦身记,怎样实现高达53%的压缩效果的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cookie简单使用方法
- 下一篇: EA生成实体类代码