讓你的apk文件儘量小,應該使移除未使用的代碼和資源文件。那麼本章節介紹瞭如何作到讓APK更小,性能更好,下載轉化率會更高,以及如何指定在構建APK過程當中保留或移除的代碼和資源,在咱們尚未分析APK大小以前,項目中存在一些資源放置處理不當,沒有統一的規範,依賴管理不合理,資源重疊,dex方法數過多等問題,致使APK文件比較大,公司要求APK體積大小要優化到3M左右。通過咱們的努力終於達到要求,然而咱們發現還能再小。javascript
正所謂工欲善其事,必先利其器,咱們得現有利器,下面就是咱們經常使用的分析APK大小工具的利器。css
Android Studio 2.2 新功能直接能分析APK的大小,雙擊打開就能看到那些佔用APK比例大,方法數等。html
使用方法:Build -> Analyz APKjava
有了Analyz APK
這個利器,如下工具也能夠基本不用了linux
NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的自動化分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析能夠分析出APK安裝包中大文件排行榜,各類知名SDK的大小以及佔代碼總體的比例,各類類型文件的大小以及佔排行,各類知名SDK的方法數以及佔全部dex中方法數的比例,針對緩慢的方法,緩慢的第三方SDK和內存泄漏。
測量生成的速度、網絡、內存和磁盤使用率。總之有很是多分析App性能的功能,若是要作性能優化,也能夠嘗試使用NimbleDroid。android
查看詳細的方法耗時git
具體使用方法請看官網:
https://nimbledroid.com/github
不過須要注意的是不要上傳任何未發佈的產品。web
ClassShark 是一款查看Android執行文件(apk)的瀏覽工具,目前有兩個android App(Apk)和桌面(jar)的版本。
使用這款工具,能夠很方便的打開APK/Class/Jar/res
等 文件和分析裏面的內容。android-studio
具體源碼與使用方法詳細在github中:
https://github.com/google/android-classyshark
經過以上任一工具分析咱們知道咱們項目中主要是如下文件佔用APK大小:
classes.dex
classes.dex是java源碼編譯後生成的java字節碼文件,
res
主要是存放咱們的圖片資源
resources.arsc
編譯後的二進制資源文件,很是多無效資源文件(語言)
assets
主要存放了咱們的緩存數據文件,已作最優化壓縮,咱們考慮可否雲端存放。
lib
主要是存放咱們的so庫,目前咱們已經優化了
既然知道了那些數據致使咱們APK體積大,那麼咱們就着手瘦身了。
對資源進行極限壓縮,主要是如res裏面用到的圖片資源文件和assets的html,db等一些緩存預留在APK的數據文件
經過以上方法咱們圖片下降了79%的大小。
這裏提供方便轉換的WEBP資源的工具:
微信中的資源混淆工具主要爲了混淆資源ID長度(例如將res/drawable/icon.png,png變成混淆爲r/s/a.png),同時利用7z深度壓縮、對png的存儲方式作了改變佔用內存更小,大大減小了安裝包體積
具體源碼與使用方法詳細在github中:
https://github.com/shwenzhang/AndResGuard
經過上面的圖片資源壓縮能對APK減少很多,但這每每還不夠,項目裏還有不少未使用的資源文件,重複的資源等,這裏主要參考Google官方文檔https://developer.android.com/studio/build/shrink-code.html#shrink-code 部分,利用Android Plugin開啓gradle 的Code shrinking
和ProGuard
結合使用。
ProGuard
可以檢測和刪除未使用的類,字段,方法,和從你的打包應用程序的屬性,包括那些包含代碼庫,ProGuard是一個混淆優化字節碼的工具,可以刪除一些未使用的代碼,混淆使用的類,字段,方法和短名稱,通過混淆處理也可以使APK源代碼獲得保護
Code shrinking
是一個Android Plugin for Gradle,從您的打包的應用程序中刪除未使用的資源,包括代碼庫中的未使用的資源。它工做在與代碼縮小,這樣,一旦未使用的代碼已被刪除,任何資源再也不引用能夠安全地刪除。
該功能須要依賴於:
code shrinking須要結合ProGuard使用,添加minifyEnabled
true
在你的build.gradle
文件中。
須要注意code shrinking會減慢Gradle 編譯,應避免使用它在您的調試版本中使用它。Android Studio禁用ProGuard使用 Instant Run.。
例如,如下從build.gradle
文件片斷,使code shrinking
爲發佈版本
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(‘proguard-android.txt'), 'proguard-rules.pro' } } ... }
其中getDefaultProguardFile(‘proguard-android.txt')
默認ProGuard設置來自於Android SDK tools/proguard/
中的文件夾
更多的代碼減小能夠嘗試使用相同位置的
proguard-android-optimize.txt
文件(這裏咱們又減小了0.5M)proguard-rules.pro是你自定義的proguard規則。
每一次build,ProGuard會輸出如下文件在/build/outputs/mapping/release/
:
在某些狀況下,默認的混淆器配置文件proguard-android.txt
文件是會移除全部只有未使用的代碼,但也有可能會誤刪除了你須要的代碼,因此要注意如下幾種狀況:
AndroidManifest.xml
配置的文件類添加-keep來忽略一下防止被混淆的代碼到proguard-rules.pro文件中,好比:
-keep public class MyClass
另外也可使用@Keep 註解在你的須要忽略的代碼中,須要Annotations Support Library的支持
有關自定義proguard-rules.pro文件的更多信息,能夠參考ProGuard Manual.這裏Troubleshooting列出了一些常見的問題。
Resource shrinking 只與Code shrinking 一塊兒工做。在代碼中刪除全部未使用的代碼後,Resource shrinking才能夠識別哪些資源的應用程序仍然使用,你必須先刪除未使用的代碼,Resource纔會成爲無用的,從而被清除掉。
添加shrinkResources
true
屬性在你的 build.gradle文件中,相應代碼塊以下:
android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
resource shrinker 目前還不支持移除定義在values/目錄下的資源文件(strings,dimensions,styles,colors),由於Android Asset Packaging Tool(AAPT)不容許Gradle Plugin指定預約義的版本資源[issue 70869]
若是您但願保留或丟棄特定的資源,請在項目中建立一個XML文件,並使用「資源」標籤,並指定每一個資源保存在工具中:保持屬性和每一個資源在工具中丟棄:丟棄屬性。兩個屬性都接受一個逗號分隔的資源名稱列表。你可使用星號做爲外卡
相應代碼塊以下:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" />
須要在項目resources目錄保存res/raw/keep.xml文件,build的時候該文件不會被打包到APK裏面。
一般狀況下,資源產品能夠準確地肯定資源使用。若是你使用Resources.getIdentifier()
動態獲取指定資源的Id,在默認狀況下,這樣資源具備匹配名稱的格式爲潛在的使用,沒法去除。
例如,下面的代碼將致使全部img_前綴的資源都沒法去除。
String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName());
resource shrinker
也經過搜索代碼中是否包含資源名來判斷是否在build的時候刪除。
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:shrinkMode="strict" />
在 resource 文件中指定 shrinkMode,你能夠指定 Gradle 在處理該資源文件時候的方式,默認的值爲 safe,你也能夠將它指定爲 strict(只會保留有明確引用的資源,以及處理被 tools:keep
和 tools:discard
標註的資源)
在後面查看資源回收狀況,咱們會講到,會遇到有些xml 沒法被清除的問題,使用shrinkMode能夠解決這個問題。
Gradle resource shrinker
只刪除你在代碼中未使用資源,這意味着它不會刪除不一樣的設備配置的可替代資源。若是有必要,你可使用Android Gradle plugin 的resconfigs屬性刪除替代資源文件。
例如:咱們項目中適配10種國家語言,而項目依賴了v七、v4等其餘support包裏面包含20種國家語言,那麼咱們能夠經過resconfigs 刪除剩餘的可替代資源文件,這對於咱們APK大小可減小了很多,
如下代碼說明了如何限制你的語言資源,只是英語和法語:
android { defaultConfig { ... resConfigs "en", "fr" } }
像上面那樣經過resconfig屬性指定的語言。未指定的語言的任何資源都被刪除。
一樣的圖片資源咱們也能夠這麼作,例如:咱們提供一套xxhdpi的圖片資源,其餘的都過濾清除掉,這樣大量清除了support,其餘第三方library的資源文件,關於這個待會咱們在後面會說。
默認狀況下,Gradle也將同名的資源,好比相同的名字,多是在不一樣的資源文件夾下,這樣子不能經過shrinkResources屬性來去除。
只有當兩個或多個文件共享相同的資源名稱、類型和限定符時才發生資源合併,關於資源的合併優先級以下:
Gradle 主要從如下位置合併資源:
Gradle合併重複資源優先順序爲:
Dependencies → Main → Build flavor → Build type
例如:若是一個重複的資源在你的mian res中和一個Build flavor指定 ,Gradle 會優先選擇Build flavor
當你Gradle resource shrinker
,Gradle Console 輸出日誌,移除APK資源的信息。例如:
:android:shrinkDebugResources Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33% :android:validateDebugSigning
APK構建完成後會Gradle會生成一個resource.txt 在 /build/outputs/mapping/release/ 中,這個文件包括詳細信息,如資源參考其餘資源和使用或刪除資源的詳細信息等。
例如:找出爲何@drawable/ic_plus_anim_016
,仍然包含在你的APK中,在resource.txt 搜索該文件名,你可能會發現它是被另外一個資源引用,以下:
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true 16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016
如今須要知道爲何@drawable/add_schedule_fab_icon_anim
仍然在使用,搜索咱們能夠知道應該有代碼引用着add_schedule_fab_icon_anim
。
若是你不使用嚴格的檢查(就是上面講的shrinkMode
),一樣的咱們若是咱們在drawable中使用了字符串’#FFFFFF‘ 這樣的使用resource shrinker
也不能將他移除在APK中,一般咱們能夠在Gradle Console中看到如下信息:
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506 used because it format-string matches string pool constant ic_plus_anim_%1$d.
那麼這種狀況咱們如何解決呢,能夠經過如下方法來清理:
利用Lint找出未使用的資源並清理掉
在Android Studio中打開「Analyze」 而後選擇」Inspect Code…」,範圍選擇整個項目,而後點擊」OK」
到這裏APK的大小又小了很多。
雖然咱們上面很好的使用了resource shrinker
能夠回收一些未使用的資源(v七、v四、google Service 等Libarry資源),但有些資源仍然未被清除。
例如:那些未使用的多套替代資源,或者是library內部隱患着引用着的資源而咱們卻沒有使用到。或者是咱們要根據用戶的手機去提供不一樣版本的APK,如分辨率(xxhdpi,mhdpi等),so庫等。那麼咱們可使用APK Splits
大大的減小一些無用的資源,這裏咱們主要參考了http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 文檔。
APK Splits
比起使用 flavors,能讓應用程序更有效地構建一些形式的多個apk。
多 apk 只支持如下類型:
使用新的APK Splits
,構建同一個應用程序的hdpi版本和mdpi版本,可以共享不少的任務 (如 javac,dx,proguard)。此外,它會被認爲是一個單一的variant,而且同一個測試程序將會被用來測試每個多APK。
android { ... splits { density { enable true exclude "ldpi", "tvdpi", "xxxhdpi" compatibleScreens 'small', 'normal', 'large', 'xlarge' } }
構建完成後能夠在out/apk/目錄下看到多個版本的APK
android { ... splits { abi { enable true reset() include 'x86', 'armeabi-v7a', 'mips' universalApk true } } }
例如:咱們項目主要提供xxhdpi的圖片資源,而項目中引用到了不少第三方庫(v七、v四、google Service 等Libarry資源)咱們沒法使用到,那麼咱們能夠經過這種方法來去除那些資源。這樣咱們的APK又減少了很是多。
Multiple APK Support
是一個在Google Play,能夠發佈不一樣的應用程序,分別針對不一樣的設備配置特徵。每一個APK是一個完整的、獨立的應用程序版本,但他們分享在Google Play相同的應用程序清單,必須共享相同的包名和與簽名。Google Play 會自動給你匹配相應的APK,這樣咱們的APK 就能夠是分不一樣版本構建須要資源文件,從而減少APK的大小。
經過發佈有多個APK,咱們能夠:
支持不一樣的CPU架構,每一個apk(如ARM、x86,MIPS等)的APK
更多相關信息請參考https://developer.android.com/google/play/publishing/multiple-apks.html#Concepts
目前咱們基於這個方案作了不一樣屏幕的APK。
咱們能夠在項目中使用資源動態加載形式,例如:表情,語言,離線庫等資源動態加載,減少APK的大小。
將來對於一些獨立業務模塊,能夠作成插件化動態加載,用戶須要使用時,只需下載少部分插件。
ReDex是Facebook開源一個減少安卓app大小以提升性能的工具,內嵌以及清除殭屍代碼這樣的優化來減少字節碼,主要是對Dex進行了優化,能讓APK 運行更快,不過須要多測試是否會崩潰。
教程很簡單具體更詳細的內容請參考:
https://code.facebook.com/posts/998080480282805/open-sourcing-redex-making-android-apps-smaller-and-faster/
github地址:
https://github.com/facebook/redex.git
關於APK瘦身就咱們就總結到這了,應該還有不少更好的方案,如有錯漏,歡迎補充。
更多Android 之美,請閱讀《Android 之美 從0到1 – 高手之路》系列文章
水平有限,如有錯漏,歡迎指正,批評,如需轉載,請註明出處–http://blog.csdn.net/vfush,謝謝!
做者:fuchenxuan
出處:http://blog.csdn.net/vfush
歡迎訪問個人我的站點:http://fuchenxuan.cn
轉載請註明出處–http://blog.csdn.net/vfush