最近幾週一直在研究如何爲APK瘦身,折騰了好久,是時候寫篇博客總結一下了,雖然已經準備了下週一要在客戶端週會分享用的PPT:APK瘦身探索。java
雖說APK瘦身對於Android對應用可分配內存的限制影響不大,可是仍是有一些影響的,就以圖片
爲例,將一些小圖標替換爲iconfont能有效減少內存的分配,防止OOM的出現。android
另外,不管是iOS開發者仍是Android開發者都應該嘗試最好學會如何爲IPA或APK瘦身,不只僅是爲了幫助用戶省流量、減小下載時間、減小佔用的存儲空間等等,更重要的是爲了提升轉化率(注意:本文的轉化率均指下載轉化率)。git
那轉化率是什麼呢?github
舉個栗子:你的應用大小是 18MB ,有100個潛在用戶想要去下載嘗試使用,結果有20個用戶嫌棄安裝包太大直接揚長而去,有20個用戶在等待下載的過程當中取消下載,最終只有60個用戶真正下載安裝,那麼應用的轉化率就是 60/100 = 60% 。web
那爲何要提升轉化率呢?api
由於用戶在糾結下載你的產品仍是你的競品的時候,每每會選擇那個體驗最好、功能最多、性能最好、包最小的。性能優化
如何有條理的爲APK瘦身,必須知道APK的目錄結構,不過在講述這個以前,我這裏先介紹幾個工具,在後文會用到。微信
AndroidStudio升級到版本2.2.3以後提供了Analyze APK的功能(不過做爲AS粉想必已經升到AS2.3了吧,哈哈),咱們能夠藉助該工具清楚的瞭解APK的下載大小、解壓以後的大小、內部各個文件夾或文件佔用的大小等信息,進而得知那些地方能夠優化。下圖爲目前達人店APK的內部信息:網絡
另外使用該工具還能夠反編譯資源文件、還原layout中的資源id,分析DEX、顯示每個文件夾或文件的方法數,分析哪些第三方庫方法數不少但實際只用到了一部分等其餘功能。
最後如何使用該工具?有如下兩種方式:
NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的自動化分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式:
其中靜態分析能夠分析出APK安裝包中大文件排行榜,各類知名SDK的大小以及佔代碼總體的比例,各類類型文件的大小以及佔排行,各類知名SDK的方法數以及佔全部dex中方法數的比例,針對緩慢的方法,緩慢的第三方SDK和內存泄漏。
其中動態分析能夠測量生成的速度、網絡、內存和磁盤使用率。
我用了一下上面這個工具,感受仍是不錯的,下面咱們來看幾張有逼格的圖(圖中數據來自於達人店APK):
APK內部各種型對應文件佔用的大小
APK內部各個類庫的方法數
APK啓動過程當中各個方法的執行時間
ClassShark 是一款查看Android執行文件(apk)的瀏覽工具,目前有兩個android App(Apk)和桌面(jar)的版本。
使用這款工具,能夠很方便的打開APK、Class、Jar、res等文件和分析裏面的內容。
不過目前這個工具並不須要咱們單獨使用,由於AS內部的分析工具就是用的這個。
經過以上工具,咱們能夠方便快速的分析APK,找出那些能夠優化的部分,爲了更加有條理的進行講解,下面我會按照APK的目錄結構來逐條闡述。
上述表格左邊部分是APK內部默認存在的文件夾或文件,表格右邊對於各個文件夾或文件進行了簡要的概述,詳細的說明會在以後根據左邊的順序一一講解。
assets目錄用於存放須要打包到應用程序的靜態文件,它包含如下幾個特性:
獲取資源須要使用/assets開始(不包含它)的相對路徑名,具體代碼以下所示:
String fileNames[] = context.getAssets().list(path);複製代碼
那麼,assets目錄下均可以放什麼文件呢?
說實在的,assets目錄能夠存放各類文件,不過正常狀況下,通常只存放如下幾種文件:字體文件、WEB頁面、配置文件、某些圖片。
上述幾種文件除了配置文件以外,咱們均可以進行適當的壓縮處理:
字體文件
:可使用字體資源文件編輯神器Glyphs進行壓縮,其壓縮方式其實就是經過刪除不須要的字符從而減小APK的大小。
WEB頁面
:能夠考慮使用7zip壓縮工具對該文件進行壓縮,在正式使用的時候解壓
某些圖片
:可使用tinypng進行圖片壓縮, 目前tinypng已經支持png和jpg圖片、.9圖的壓縮
lib目錄用於存放經過C或C++編寫編譯生成的so文件(native庫/JNI開發),其基本目錄結構以下圖所示:
由於目前市場上主流的架構還只是arm架構,因此若是不是必要的話,能夠考慮不支持x86和mips架構,但這並不意味着CPU是x86或mips架構的手機就不能正常安裝使用APK了,由於放在arm目錄下的so庫是能夠兼容到其餘架構的;
另外arm架構中的eabi-v7a相比於eabi只是在圖形渲染方面有了很大的改進,因此若是so庫對圖形渲染沒有很高的要求的話,徹底能夠把so庫只存放在arm eabi目錄中,這樣能夠大大減少APK的體積。
上面的說明可能比較籠統,下面就拿淘寶、微信的APK來講明一下:
能夠看出淘寶和微信也是這樣處理,因此咱們其實也能夠這樣操做。
res目錄用於存放應用程序的資源文件,主要包括佈局文件、圖片、XML配置文件等,它包含如下幾個特性:
其基本目錄結構以下圖所示:
上圖比較全面的列舉了res目錄下常常包含的子目錄和文件,並解釋了各個子目錄或文件的含義。
根據上圖咱們顯然能夠發現,res目錄就是咱們APK瘦身裏面的一大重要部分了,因爲其包含的知識很大,下面我會分章節進行闡述,儘可能一一闡述清楚。
首先我給出這麼一個結論:一個APK儘可能只用一套圖片,從內存佔用和適配的角度考慮,建議放在xhdpi文件夾下。
那麼爲何要把圖片放在xhdpi文件夾下面呢?
因爲此處知識涉及到Android屏幕適配方面的知識,比較複雜。本文是關於APK瘦身的,因此就不詳細講解了。下面進行必要的闡述,首先讓咱們來看兩張圖:
從上面兩張圖咱們能夠獲得如下兩點信息:
可能有人要問了,獲得這兩點信息有什麼用?
恩,單單這樣看並不能知道什麼,爲了幫助你們瞭解,我繪製了下面這樣表格。不過在展現表格以前,我先爲你們灌輸兩個知識點:
一、ppi計算公式
二、在Android設備中,dpi 等價於 ppi
恩,此處請停留10秒......下面展現我繪製的表格:
有兩個注意點:
根據上述表格咱們能夠清楚的知道iOS流行的設備分辨率正好對應Android流行的設備分辨率,那麼知道這又有什麼用?
除了一開始給出結論的內存和適配因素外,更重要的是能夠節省設計資源和工做量。在如今的App開發中(iOS和Android),有些設計師爲了保持iOS和Android的體驗交互一致,可能會以iPhone手機爲基礎進行設計,包括後期的切圖之類的。
不過根據上述表格,咱們不是應該使用兩套圖嗎?這裏因爲在Android設備中xhdpi和xxhdpi目錄下的圖片顯示效果差別不大,因此徹底能夠只使用一套圖。
如今咱們的設備中只有一套圖了,接下來該怎麼爲APK瘦身呢?
當咱們從設計師手中獲得設計稿以後,以後的工做顯然就是切圖了,若是設計師切好圖就更好了,獲得圖片以後咱們必定要記得壓縮
。
若是圖片類型是.png或.jpg的話,咱們可使用tinypng進行壓縮;若是圖片類型是.gif的話,我建議使用PS進行壓縮或裁剪,若是你不會PS的話,可讓設計師幫忙。
若是你對圖片壓縮質量不滿意的話,還能夠考慮使用不帶alpha值的jpg圖片、9Patch圖片、同等質量下文件更小的WebP圖片格式、或者使用SVG替換某些圖片資源等其餘方式。
下面對於最後兩種方式進行簡要的闡述:
首先是WebP
:AndroidStudio自2.3版本以後提供了Convert to WebP的功能,選中res目錄後右擊滑到底部便可看到此功能,下面的引用講述了WebP的概念和支持度:
WebP是Google新推出的影像技術,它可以讓網頁圖檔有效進行壓縮,同時在質量相同的狀況下,WebP格式圖像的體積要比JPEG格式圖像小40%,進而讓總體網頁下載速度加快。爲了改善JPEG的圖片壓縮技術,他們使用了一種基於VP8編碼的圖片壓縮器,利用預測編碼技術,同時還採用了一種基於RIFF的很是輕量級的容器。這種容器只會給每張圖片增長20字節,但能讓圖片做者保存他們想要存儲的元數據。
android一樣做爲google的產品,minsdk爲4.0即api14以上就能夠支持webp,可是對透明的圖片會存在一些問題,minsdk爲4.2.1+即api17能夠完美支持webp。
考慮目前市場4.2.1如下的手機佔比已經很是稀少,採用webp格式代替jpg、png的方案很是可行。
下面展現一下收益頁面背景圖片png形勢下和WebP形勢下各自的大小:
其次是SVG
:SVG是可縮放矢量圖形(Scalable Vector Graphics),它使用XML格式定義圖像,可用於替換APK中的圖標。
咱們開發者不須要會如何製做,這是設計師的工做,固然設計師也不會傻乎乎經過寫代碼的方式繪製圖標,顯然是使用軟件製做的。
目前達人店也使用了SVG,不過它的表現形式是阿里巴巴提供的iconfont,它不只容許設計師本身製做SVG圖片上傳,也提供了百萬種圖標供選擇。
通過上述的方式處理圖片,想必在圖片質量方面已經機關用盡了,因此咱們只能經過刪除無用圖片的方式來爲APK瘦身了,固然接下來要介紹的兩種方式可不只僅是減小圖片,應該稱之爲減小資源
。
這裏有個問題:爲何會出現無用的資源呢?我認爲有如下幾種狀況:
那麼,既然知道了緣由,就應該想出辦法去解決,首先是最簡單的辦法,咱們只須要在主模塊的gradle文件中配置shrinkResources
便可,具體配置方法以下圖所示:
這就是爲何說它是最簡單的減小資源方法的緣由。
固然,由於簡單必然存在缺陷,由於它只能從打包的應用程序和第三方代碼庫中刪除未被引用的資源。若是在不一樣的資源文件夾下面有同名的資源文件,那麼就沒有辦法刪除了,即便該資源真的沒有被使用。
因此,沒有好的辦法了,目前我能想到的只能是手動刪除了,咱們能夠經過AndroidStudio提供的Remove Unused Resources功能預覽刪除真正未使用的資源,選中res目錄右擊選擇Refactor-Remove Unused Resources...選項以後會出現下面這樣的圖:
而後根據查找到結果,逐條雙擊打開文件,按快捷鍵fn+option+F7進行查找,而後在分析是否應該刪除,注意:此處定要慎重,務必自測!!!。
另外,咱們還能夠經過將某些圖片轉換網絡圖片的方式解決,可是這個操做也是須要慎重的,最重要的一點是不能影響用戶的體驗。這邊路飛同窗製做了一個Chrome插件來幫助咱們快速上傳圖片到cdn中,能夠經過此處下載:joyuploader。
最後,還有一種辦法:使用AndroidStudio提供的Lint工具對工程作靜態代碼檢查,它不只能夠找出咱們在代碼編寫上面的失誤,還可以列出那些未被使用的資源,甚至還能夠指出哪些地方可能存在內存泄漏等等,功能很是龐大,因此若是工程較大的話,仍是比較耗時的,固然這並非問題,由於咱們能夠針對某個模塊執行靜態代碼檢查。咱們能夠經過選中菜單欄Analyze-Inspect Code...選項執行靜態代碼檢查,執行完成的效果圖以下所示:
目前咱們不只在資源(特指圖片)質量方面作到了極致,在資源數量方面也作到了極致,看起來真的到極致了。其實否則,咱們還可使用資源混淆的方式爲APK瘦身,經過壓縮文件內容,減小文件名長度的方式實現。
目前比較出名的資源混淆方式是微信的AndResGuard和美團的修改AAPT,不過因爲美團的資源混淆方法很是麻煩,還有若是須要經過getIdentifier
的方式獲取資源時須要保證這些資源的名字不被混淆,美團很難實現,因此目前你們都在用微信的資源混淆方式,由於微信使用方便,並且提供了白名單。
下面是達人店APK未使用微信資源混淆和使用了微信資源混淆的差別:
能夠清楚的看到,在使用了微信資源混淆以後,APK減小了0.7MB左右,效果仍是十分明顯的。
那麼爲何使用了微信資源混淆以後能夠實現APK瘦身呢?下面這兩張圖清楚的展現了瘦身的緣由(摘自微信資源混淆官方文檔):
總結,安裝包大小減小的緣由如下四個:
相對的,可獲得影響效果的因素有如下幾個:
能夠說,直到如今,咱們對於資源的壓縮猜到了一個極致,下面是一些針對於某些資源的壓縮辦法。
終於講完了res部分,碼字好累,接下來就相對輕鬆了,首先咱們要講解的是classes.dex文件。
首先,什麼是classes.dex?classes.dex文件是Android系統的可執行文件,包含應用程序的所有操做指令以及運行時數據。
這裏稍微介紹一下classes.dex文件的生成過程及對應的命令:
java源代碼 -> class字節碼:[javac -source 1.6 -target 1.6 com/package1/*.java com/package2/*.java]
class字節碼 -> jar包:[jar cvf abc.jar com/package1/*.class com/package2/*.class]
jar包 -> dex文件:[dx --dex --output ***/abc.jar ***/abc_dex.jar],***是abc.jar的絕對路徑。
從jar包到dex文件的過程是經過dx工具完成的,它的目的是使各個類可以共享數據,在必定程度上下降了冗餘,同時也使文件結構更加緊湊,實驗代表,dex文件是傳統jar文件大小的50%左右,具體表現形式能夠看下圖:
既然dx工具已經辦幫咱們壓縮了那麼多,那咱們還有什麼好壓縮的呢?
固然有,由於dx工具並無壓縮class的內容,因此咱們的源代碼並無獲得壓縮,下面我先介紹兩種常見的辦法來解決這個問題:
和以前最簡單的資源壓縮方式同樣,咱們也能夠在主模塊的gradle文件中配置minifyEnabled
實現代碼混淆,從而減少java文件的大小。由於混淆後的代碼將較長的文件名、實例、變量、方法名等等作了簡化,從而實現字節長度上的優化。
咱們也可使用以前所說的AndroidStudio提供的Lint工具執行靜態代碼檢查,進而刪除無用的類、方法、變量等。
上面的兩種方式雖然可以幫助咱們減小APK的大小,可是實際上咱們還能夠作的更多。由於上面兩種方式是自動化的,因此必然不像人那麼智能。
有時候咱們可能遇到這樣的狀況:咱們引用了一個第三方類庫,可是隻用到了其中的幾個功能,其餘的大部分功能一直不用,這不是白白的浪費了用戶的流量,下降了APK的下載轉化率麼?爲了解決這樣的問題,咱們必須藉助一些工具去找到這樣的類庫,好比在文章開頭介紹過的工具:NimbleDroid,下面是達人店APK目前存在的某個這樣的第三方類庫:
該類庫在達人店APK中只用到了掃一掃和生成二維碼這兩個功能,然而這個類庫定義的方法數居然有1428
,顯然不合理,徹底能夠抽離其內部的掃一掃和生成二維碼代碼,單獨實現。
最後介紹一下Facebook開源的一個減小APK大小以提升性能的工具 -- ReDex,它經過內嵌以及清除殭屍代碼這樣的優化方式來減小字節碼,其主要是對DEX作了優化,可以讓APK運行更快,不過須要多測試是否會崩潰。
下面分別是ReDex的教程地址和GitHub地址:
facebook/redex: A bytecode optimizer for Android apps
我我的因爲時間問題,目前還未接入該工具,下面就展現一下網友u012124438的測試數據好了:
後來我在使用 Redex 壓縮和優化 Android APK一文的幫助下,成功的安裝並使用了ReDex,可是發現瞭如下兩個問題:
最後就是對於resources.arsc文件的壓縮處理了,其實這裏咱們也不須要作什麼,首先由於該文件記錄的是資源文件和資源ID的映射關係,並無什麼值得壓縮的地方;另外若是真的有值得壓縮的地方,也只有資源文件的名字了,不過這個早就在以前由於咱們使用了微信資源混淆解決了,因此此處就再也不多講了。
至此,針對於APK目錄結構,逐條分析如何爲APK瘦身已經講完了,接下來介紹一下其餘的壓縮方式。
雖然咱們上面很好的使用了resource shrinker能夠回收一些未使用的資源(v七、v四、google Service 等Libarry資源),但有些資源仍然未被清除。
例如:那些未使用的多套替代資源,或者是library內部隱患着引用着的資源而咱們卻沒有使用到。或者是咱們要根據用戶的手機去提供不一樣版本的APK,如分辨率(xxhdpi,mhdpi等),so庫等。那麼咱們可使用APK Splits大大的減小一些無用的資源,這裏咱們主要參考了tools.android.com/tech-docs/n…文檔。
Multiple APK Support是一個在Google Play,能夠發佈不一樣的應用程序,分別針對不一樣的設備配置特徵。每一個APK是一個完整的、獨立的應用程序版本,但他們分享在Google Play相同的應用程序清單,必須共享相同的包名和與簽名。Google Play 會自動給你匹配相應的APK,這樣咱們的APK 就能夠是分不一樣版本構建須要資源文件,從而減少APK的大小。
咱們能夠在項目中使用資源動態加載形式,例如:表情,語言,離線庫等資源動態加載,減少APK的大小。
將來對於一些獨立業務模塊,能夠作成插件化動態加載,用戶須要使用時,只需下載少部分插件。
特別是在扁平化盛行的當下,不少純色的漸變的圓角的圖片均可以用shape實現,代碼靈活可控,省去了大量的背景圖片。
相信你的工程裏也有不少selector文件,也有不少類似的圖片只是顏色不一樣,經過着色方案咱們能大大減輕這樣的工做量,減小這樣的文件。
藉助於android support庫可實現一個全版本兼容的着色方案,參考代碼:DrawableLess.java
若是你不是一個Android開發人員,對於以前的闡述可能很是模糊,並不能明顯的表現出APK瘦身這件事情的意義,下面我就來總結一下:
首先,針對於達人店APK作了哪些應用?
字體文件使用Glyphs進行壓縮,圖片使用tinypng進行壓縮
只是用一套so文件
只使用一套圖
使用WebP圖片或SVG圖片替換某些PNG或JPG圖片
使用Google提供的Gradle插件實現代碼混淆和資源混淆
藉助NimbleDroid、AS Anylze/Lint工具分析查找刪除不須要的代碼或資源
使用微信提供的資源混淆工具(處於穩定性測試階段)
使用Facebook提供的ReDex工具(處於調研階段)
其次,APK瘦身的效果如何?
這裏咱們就直接看圖好了:
能夠明顯的發現,達人店APK愈來愈小了,因此說,這個工做是頗有意義的。
最後,作一下競品分析?
根據上面圖表,能夠發現咱們的APK的大小是十分有優點的,可以很好的提升下載轉化率。而iOS那邊顯然就比較大了,雖然二者之間不能比較,可是仍是能夠進行一系列優化的。
最後的最後,我想對你們說:在APK瘦身的道路上,必定要掌握好度
,安排好事情的優先級,若是目前要作的事情、要優化的方面比較複雜,不只須要花費很長的時間,並且最終效果也不明顯,能夠考慮以後再作,甚至不作。