若是你對App優化比較敏感,那麼Apk安裝包的大小就必定不會忽視。關於瘦身的緣由,大概有如下幾個方面:javascript
下表爲Apk目錄及文件說明:java
文件/目錄 | 說明 |
---|---|
assets/ | 存放一些靜態文件,能夠經過AssertManager訪問 |
lib/ | 若是該目錄存在,通常存放的是NDK編譯出來的so |
META-INF/ | 保存着APK的簽名信息 |
res/ | 資源文件所在目錄,包含drawable、layout等 |
AndroidManifest.xml | 程序全局配置文件 |
classes.dex | Java Class,被DEX編譯後可供Dalvik/ART虛擬機所理解的文件格式 |
resources.arsc | 編譯後生成的二進制資源文件 |
作App瘦身以前須要對本身App現有組成有一個清晰的認識,上述解壓的方式只能粗略的看出具體目錄的大小,可是有用信息仍然有限。android
Android Studio 2.2以後有一個功能Analyze APK,方便簡單,功能仍是Google自帶的靠譜;git
ClassShark 是一款查看Android執行文件(apk)的瀏覽工具,能夠很方便的打開APK/Class/Jar/res等文件和分析裏面的內容。github
NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析能夠分析出APK安裝包中大文件排行榜,各類知名SDK的大小以及佔代碼總體的比例,各類類型文件的大小以及佔排行,各類知名SDK的方法數以及佔全部dex中方法數的比例。web
總結:這三種方式均可以對Apk的組成有一個更加清晰的認識,但更加推薦使用AndroidStudio自帶的Analyze APK,簡單、高效。算法
使用Analyze APK查看到文件大小以後發現,classes.dex、res、assets、lib等文件較大,哪裏的脂肪多,咱們就去抽哪裏。肯定優化方向:android-studio
隨着版本的迭代,部分功能可能已被去掉,可是其代碼還存在項目中。移除無用代碼以及無用功能,有助於減小代碼量,直接體現就是Dex的體積會變小。性能優化
備註:根據經驗,不用的代碼在項目中存在屬於一個廣泛現象,至關於殭屍代碼,並且這類代碼過多也會致使Dex文件過大。微信
備註:根據經驗,項目中存在以前使用以後不使用的庫的狀況並不罕見。
代碼混淆也稱爲花指令,是將計算機程序的代碼轉換爲功能上等價可是難以閱讀、理解的行爲。Proguard是一個免費的Java類文件壓縮、優化、混淆、預先驗證的工具,能夠檢測和移除未使用的類、字段、方法、屬性,優化字節碼並移除未使用的指令,並將代碼中的類、字段、方法的名字改成簡短、無心義的名字。
能夠看出Proguard不只能將diamante中的各類元素改的簡短,還能夠移除冗餘代碼,所以能夠減小Dex文件的大小。
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(‘proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}複製代碼
其中,proguard-android.txt是獲取默認ProGuard設置,proguard-rules.pro文件用於添加自定義ProGuard規則。
備註:對於Proguard,雖然效果很明顯,但仍然須要謹慎;
通常狀況下縮減方法數,都是爲了Android著名的64k方法數問題,此處再也不回顧,參見以前《關於Multidex的系列文章》。而這裏說縮減方法數的目的,是爲了App瘦身。
經過《Dalvik Executable format》,咱們能夠看到Dex文件的組成。而從header-item表中的method-ids-size字段能夠看出,方法數縮減以後,能夠減小方法列表的大小;同時,方法在Dex文件中的佔用空間也減小了,App天然被瘦身。
而縮減方法數,除了上面寫到的廣泛方法:移除無用方法、庫、使用較小的SDK以外還有:
對於重要性,代碼和資源的瘦身一樣重要,可是從效果上來講,資源文件的瘦身效果比代碼的瘦身效果要好很是多。頗有可能費力許久在代碼上獲得的瘦身效果,在資源文件瘦身中輕鬆就獲得了。
移除無用資源文件要比移除無用代碼容易,在Android Studio的任何文件中右擊,選擇清除無用資源便可刪除沒有用到的資源文件。
備註:在build.gradle中設置shrinkResources爲true後,每次打包的時候就會自動排除無用的資源。shrinkResources須要配合minifyEnabled一塊兒使用。可是根據個人實驗:無用的資源仍是會被打進Apk中,只是變成一張黑圖,體積也很是小,只有不到100b。有使用錯誤的地方歡迎指正!
這條開發者中討論的比較多,確實Google強烈建議根據不一樣屏幕密度準備多套切圖資源來作適配的。可是鑑於Android上對UI要求不會是最頂級的那種高度,以及即使是放在合適(注意這兩個字)一個的目錄下,在不一樣的分辨率下也會作自動的適配(等比例拉伸、縮放);所以仍是建議:對UI不是最頂級要求的話根據本身的用戶羣體機型放在一個合適的目錄下。這樣毋庸置疑能夠縮減Res的大小,進而減小Apk的體積。
備註:圖片放在不恰當的目錄有可能會對內存產生較大的影響,能夠參考以前的文章《Android 性能優化(五)之細說 Bitmap》。
以前我在項目裏發現過文件大小過1M的圖片,多是因爲UI同窗和RD同窗的雙重疏忽,致使如此大的圖片到了項目中,對Apk體積的影響天然不言而喻。
能夠考慮使用TinyPng、pngquant、ImageOptim等工具對圖片進行壓縮,這些工具能夠減小PNG文件大小,同時保持圖像質量。
此處以TinyPng爲例:TinyPng是一個至關不錯的圖片壓縮工具,在保持alpha通道的狀況下對PNG的壓縮能夠達到1/3以內,並且用肉眼基本上分辨不出壓縮的損失。這張3.4M的圖片被壓縮到了984.7k,壓縮率高達71%。
也有同窗開發了一個AndroidStudio插件:TinyPngPlugin,可以批量地壓縮項目中的圖片,更加方便。
備註:須要注意的是在Android構建流程中AAPT會使用內置的壓縮算法來優化res/drawable/目錄下的PNG圖片,但也可能會致使原本已經優化過的圖片體積變大,能夠經過在build.gradle中設置cruncherEnabled來禁止AAPT採用默認方式優化咱們已經優化過的圖片。
aaptOptions {
cruncherEnabled = false
}複製代碼
PNG是一種無損格式,JPG是有損格式。JPG在處理顏色不少的圖片時,根據壓縮率的不一樣,有時會去掉一些肉眼識別差距較小的中間顏色。可是PNG對於無損這個基本要求,會嚴格保留全部的色彩數。因此圖片尺寸大,或者色彩數量多特別是漸變色的多的時候,PNG的體積會明顯大於JPG。
在這種狀況下,咱們能夠有所取捨。小尺寸、色彩較少或者有alpha通道透明度的時候,使用PNG;大尺寸、色彩漸變多的使用JPG。
備註:根據經驗,對於能夠直接使用JPG格式的圖片,最好不要從PNG轉換爲JPG,而是出圖的時候直接出JPG格式的圖片,相對而言,後者的效果更好。
可縮放矢量圖形(英語:Scalable Vector Graphics,SVG)是一種基於可擴展標記語言(XML),用於描述二維矢量圖形的圖形格式。SVG由W3C制定,是一個開放標準。可使用矢量圖形來建立獨立於分辨率的圖標和其餘可伸縮圖片。使用矢量圖片可以有效的減小App中圖片所佔用的大小,矢量圖形在Android中表示爲VectorDrawable對象。
優勢
缺點
Google於2010年提出了一種新的圖片壓縮格式 — WebP,爲圖片提供了無損和有損壓縮能力,同時在有損條件下支持透明通道。據官方實驗顯示:無損WebP相比PNG減小26%大小;有損WebP在相同的SSIM(Structural Similarity Index,結構類似性)下相比JPEG減小25%~34%的大小;有損WebP也支持透明通道,大小一般約爲對應PNG的1/3。同時,谷歌於2014年提出了動態WebP,拓展WebP使其支持動圖能力。動態WebP相比GIF支持更豐富的色彩,而且也佔用更小空間,更適應移動網絡的動圖播放。
優勢:
缺點:
在Apk打包過程當中,aapt會將每個資源生成一個對應的int數值,而咱們經過這個int值來查找使用資源。在Apk構成中,咱們能夠看到裏面有一個resources.arsc文件,裏面保存着資源id和資源key的映射關係。
當調用圖片時,先找到drawable分類,再根據當前的系統config找到匹配的config表,根據id找到對應的res數據。drawable在arsc中是當作string類型保存的,res數據中有這個資源在res string pool池中的索引。根據這個索引能夠在字符串池中找到一個字符串。這個字符串其實就是一個路徑,好比:res/drawable-xhdpi/icon.png;混淆就是將這個路徑改成R/s/f.png;同時修改resources.arsc文件的映射關係。這樣就能清楚的看出來資源混淆能減少Apk的緣由:
這裏推薦微信的資源混淆方案:AndResGuard。
將部分使用頻率不高的資源例如圖片,放在網上,在恰當的時機提早下載,這樣也能節約部分空間。
So(shared object,共享庫)是機器能夠直接運行的二進制代碼,是Android上的動態連接庫,相似於Windows上的dll。每個Android應用所支持的ABI是由其APK提供的.so文件決定的,這些so文件被打包在apk文件的lib/目錄下。
So的常見的場景如:加解密算法、音視頻編解碼、核心代碼等。在生成SO文件時,須要考慮適配市面上不一樣手機CPU架構,而生成支持不一樣平臺的SO文件進行兼容。目前Android共支持七種不一樣類型的CPU架構,分別是:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),Mips (從2012年起),ARMv8,Mips64和x86_64 (從2014年起)。
理論上對應CPU架構的So的執行效率是最高的,可是這樣會致使在libs目錄下放置各個架構平臺的So文件,Apk文件的大小天然也就更大了。那麼咱們天然想到縮減Libs的目錄,通常狀況(注意限定)下留下armeabi目錄便可,armeabi目錄下的So能夠兼容別的平臺的So,可是性能會有所損耗,失去對特定平臺的優化。
所以須要根據本身使用到的So功能來作具體的區分:對於性能敏感模塊使用的So能夠都放在armeabi目錄,而後經過代碼判斷設備的CPU類型,再加載其對應架構的SO文件,例如微信就是這麼作的。既縮減了Apk的體積,也不影響性能敏感模塊的執行。
移除特定平臺So的方式,這樣打包就只保存armeabi裏的So。
ndk {
//設置支持的SO庫架構
abiFilters 'armeabi'
}複製代碼
備註:本來x86架構的CPU是不支持運行arm架構的So,但Intel和Google合做在x86機子的系統內核層之上加入了一個名爲houdini的Binary Translator(二進制轉換中間層),這個中間層會在運行期間動態的讀取arm指令並將之轉換爲x86指令去執行。
咱們知道Apk文件實際上就是一個Zip文件。Android SDK的打包工具apkbuilder採用的是Deflate算法將Android App的代碼、資源等文件進行壓縮,壓縮成Zip格式,而後簽名發佈。
既然是壓縮,那能不能改進其壓縮方式,獲取更小的Apk文件?經過分析Apk打包的流程圖咱們能夠發現SignedJarBuilder類對整個工程包括代碼Dex和一些課壓縮的資源、文件進行壓縮,使用的是JDK中zip包下提供的算法。
簡單的方式咱們能夠在不改變App編譯器工做的狀況下,對生成的Apk文件進行二次壓縮,一樣使用Deflate算法,可是將壓縮等級從標準提高到極限壓縮。提升壓縮級別可在不對Apk包自己的內容作任何修改的狀況下獲得更小的Apk。
備註:
使用7Zip對Apk進行極限壓縮。
參考:
歡迎關注微信公衆號:按期分享Java、Android乾貨!