做者:非戈@阿里移動安全,更多技術乾貨,請訪問阿里聚安全博客java
APK是Android系統安裝包的文件格式,關於這個話題實際上是一個老生常談的題目,不管是公司內部,仍是外部網絡,前人前輩已經總結出不少方法和規律。不過隨着移動端技術近兩年的飛速發展,一些新的思惟方式和優化方法也逐漸涌現和成熟起來。筆者在實踐過程當中踩過一些坑,收穫了一些經驗,在這裏作個思考和總結,因此隨筆給你們,但願對你們從事相關工做的時候有所幫助和參考,同時也是拋磚引玉,但願你們共同探討這個開放性的話題。android
關於爲何APK要瘦身,這個很少說,只從三個方面嘮叨一下,對於用戶(或者客戶)來講,APK越大,在下載安裝過程當中,他們耗費的流量會越多,安裝等待時間也會越長;對於產品自己,意味着下載轉化率會越低(由於競品中,用戶有更多機會選擇那個體驗最好,功能最多,性能最好,包最小的);對於研發來講,是一種優化改進技術的機會。git
欲瘦身,咱們先找找胖的緣由和問題。按目標-路徑-資源的思惟模式,找緣由和問題有以下幾條路徑,一是拍腦殼,按本身的經驗和判斷,甚至是主觀想象;二是去搜索引擎找關鍵字,逛各類技術論壇聽技術大牛們怎麼說,看各種技術文章抽取提煉;三是用一種可測量的工具或者方法發現問題。github
前兩種不贅述,我這裏說說第三種方法。用一種可測量的工具或者方法來分析,所謂工欲善其事,必先利其器。這個器能夠能夠本身鍛造,也能夠用現成的。這裏推薦一個在線apk分析工具,由於是外部工具,因此你們請在使用過程當中,不要上傳未發佈出去的產品,爲了數據安全,筆者這裏拿一個github上開源的Android項目做爲瘦身示例。web
NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析能夠分析出APK安裝包中大文件排行榜,各類知名SDK的大小以及佔代碼總體的比例,各類類型文件的大小以及佔排行,各類知名SDK的方法數以及佔全部dex中方法數的比例,廢話很少說,下面上高清無碼大圖看看顏值吧。api
若是想使用分析功能分析本身的產品,請登陸並上傳本身產品的apk包,全部功能目前均無償使用,若是是想分析Google Play上已經發布的產品,能夠直接點擊"Play Apps"查看,還可使用搜索功能根據應用名和包名查看結果。再次強調下,請不要上傳任何未發佈的產品。安全
登陸網絡
上傳apk文件app
分析結果摘要,能夠看到一些概覽的信息,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內剖圖是否是有一種神清氣爽的感受!我把這個分析工具比作咱們家買的智能體重秤,能夠稱體重,脂肪含量,骨重,骨密度,肌肉含量等等,那麼,咱們是否是發現了一些問題,進而把這些問題和咱們以前靠經驗和一拍腦殼的緣由能夠用邏輯聯繫在一塊兒。
那麼,咱們接下來能夠經過分析數據整理出咱們的優化目標
大文件排行榜裏,有11張png文件的大小超過了100k,記住,這但是壓縮以後的啊;
大文件排行榜裏,resources.arsc的大小接近2M,這也是一個優化點;
大文件排行榜裏,classes.dex接近3M,classes.dex是代碼的載體,這塊的優化須要細分,再去看看細分SDK的排行榜;
組件佔比環圖裏,Android Support, Jackson JSON Parser和Google Play Services是三方庫的前三甲;
文件類型排行榜裏,png, dex 和arsc是前三甲;
因此咱們的目標是沒有蛀牙,不對,是下面的目標:
png圖片優化;
resources.arsc文件的優化;
代碼優化
首先是第一個目標,圖片的優化,慢點,咱們看看這些圖爲何這麼大先,準確的說,爲何這些圖在apk(其實就是zip文件)裏這麼大,好了,上工具分析。
此次用了一些簡單的工具組合,系統自帶的cmd就好。
$ unzip -l -v vanilla/build/outputs/apk/vanilla-prod-release.apk | grep png | sort -r
命令執行的結果以下
恩,全部的png文件竟然是STORE的方式存儲到apk裏的,關於zip裏的STORE和DEFLATE,詳見 )
通俗的說,當文件是STORED的方式存儲到zip,表示這個文件並無通過壓縮,若是是Defl:N的方式,表示經過DEFLATED normal的方式壓縮存儲到zip。
這看起來有點不合理,png原封不動的放入zip,固然最後產出的apk會比較大。那麼,如何解決呢?筆者首先嚐使用android gradle plugin的方式,發現aaptOptions和packagingOptions都未能解決問題。在github上發現一個開源項目AndResGuard,試了集成到項目中,再看結果以下:
優化前:
10536027字節
優化後:
普通zip壓縮: 8786265字節 (壓縮了將近17%)
採用7zip壓縮:8567150字節 (壓縮了將近19%)
再看看這個工具作了什麼,對比下開啓資源混淆先後
優化前
優化後
資源(png, xml, jpg等)名稱混淆,資源路徑名稱混淆以及名稱長度壓縮;
原來以STORED形式存儲到zip中的png文件被改爲了DEFLATED(普通壓縮存儲)方式;
意外發現resources.arsc, META-INF/.SF 以及 META-INF/.MF變小了,並且是解壓以後的文件大小也變小了。
用apk反編譯神器jadx內窺apk尋找真相
原來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配置示例以下:
defaultConfig { resConfigs "en" ndk { abiFilters 'armeabi' } }
記得包在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
腳本參考以下
android { buildTypes { release { debuggable false minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-project.txt' zipAlignEnabled true } } }
shrinkResources配合minifyEnabled使用效果更佳,詳見shrinkResources用法以及注意
採用7zip壓縮:8115283字節 (壓縮了將近23%,再增長1個點)
3.使用圖片壓縮工具,壓縮png圖的大小,將非alpha的圖轉換成jpg形式,關於這點同事以及網絡上的大牛們已經整理的很詳細了,我這裏作簡單總結,欲知詳情,請見附錄的參考。
使用tinypng,我只想說我們在公司作產品,此方案慎用,上傳任何未發佈產品的內容到外部網絡,都有可能引發數據泄漏,因此慎用此方案。下面說替代方案。
WASTED
pngquant
ImageAlpha
ImageOptim
以上工具太散,有沒有集成化的工具,答案是「有」,@心倫 童鞋開發的imagemin
@姐夫童鞋開發的MSImageResourcesHelper
png轉成jpg格式 具體效果不等。
4.終極大殺器,png轉成webp,關於webp,更多詳情請參考谷歌官方文檔以及安卓開發者在線參考
先上效果圖:
採用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,因此致使加載資源文件失敗,初步斷定,目前暫時沒有解決方案,因此只能忍痛放棄這個優化方案。
關於第一個目標,圖片資源的優化,就寫到這裏了。
第二個目標已達成,剩下第三個目標,代碼的優化,梳理以下優化路徑:
開啓proguard的代碼優化 將proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-project.txt'
改成proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-project.txt'開啓代碼優化後的注意點請參見附錄。
去除無用的庫
若是apk支持的最低版本是API14,而代碼中沒有用到高於api14的api就能夠考慮拿掉整個android support庫。
用更小的庫替代方案
若是隻用到了谷歌統計,那麼就不要把整個google play services都集成進來,只集成須要的部分。
按期清理廢棄的代碼
按期刪除無用的邏輯和過時的業務功能模塊,以及廢棄的A/B test代碼。
業務模塊採用插件化框架,代碼動態從雲端拉取
插件化,這是另一個課題了,這裏不贅述。
**apk瘦身記最終的成果
10536027字節壓縮到4926912字節, 壓縮了將近53%**
華麗麗的分界線~~~~~~~~如下是總結~~~~~~~~~~~~~~~~~~~~
腳本中開啓資源混淆和資源壓縮
用7zip代替zip
gradle腳本中開啓代碼混淆優化和無用資源刪除
用更小的圖,使用壓縮工具壓縮圖片大小
去除無用的資源,語言,本地so庫,二方三方庫和分辨率
用更小的庫
嘗試將android support庫完全踢出你的項目
按期清理代碼
嘗試用H5編寫界面,圖片雲端獲取
嘗試插件化業務模塊
尋找到zip文件夾中全部用STORE形式存儲的文件(不限於raw目錄下),嘗試壓縮,以及替代方案加載這些資源
嘗試webp的圖片加載方案,尋求突破
最後,繼續學習和嘗試新的優化方案
以此文獻給「惟瘦身與產品不可辜負」的技術們!!!
如何作到將apk大小減小6M
Android APP終極瘦身指南
APK瘦身實踐
做者:非戈@阿里移動安全,完整版文章內容及更多技術乾貨,請訪問阿里聚安全博客