[Android 之美] 那些你不知道的APK 瘦身,讓你的APK更小

[Android 之美] APK 瘦身,減小APK的大小

讓你的apk文件儘量小,應該使移除未使用的代碼和資源文件。那麼本章節介紹瞭如何作到讓APK更小,性能更好,下載轉化率會更高,以及如何指定在構建APK過程當中保留或移除的代碼和資源,在咱們尚未分析APK大小以前,項目中存在一些資源放置處理不當,沒有統一的規範,依賴管理不合理,資源重疊,dex方法數過多等問題,致使APK文件比較大,公司要求APK體積大小要優化到3M左右。通過咱們的努力終於達到要求,然而咱們發現還能再小。javascript

分析APK的大小

正所謂工欲善其事,必先利其器,咱們得現有利器,下面就是咱們經常使用的分析APK大小工具的利器。css

使用Android Studio 2.2

Android Studio 2.2 新功能直接能分析APK的大小,雙擊打開就能看到那些佔用APK比例大,方法數等。html

這裏寫圖片描述

  • 分析任何的APK
  • 查看APK下載包的大小,解壓後的實際大小
  • 反編譯資源文件,還原layout中的資源id,代碼
  • 分析dex,顯示每部分的方法數,直接查看那些library體積比較大

使用方法:Build -> Analyz APKjava

有了Analyz APK這個利器,如下工具也能夠基本不用了linux

NimbleDroid

NimbleDroid 是美國哥倫比亞大學的博士創業團隊研發出來的自動化分析Android app性能指標的系統,分析的方式有靜態和動態兩種方式,其中靜態分析能夠分析出APK安裝包中大文件排行榜,各類知名SDK的大小以及佔代碼總體的比例,各類類型文件的大小以及佔排行,各類知名SDK的方法數以及佔全部dex中方法數的比例,針對緩慢的方法,緩慢的第三方SDK和內存泄漏。
測量生成的速度、網絡、內存和磁盤使用率。總之有很是多分析App性能的功能,若是要作性能優化,也能夠嘗試使用NimbleDroidandroid

查看詳細的方法耗時git

具體使用方法請看官網:
https://nimbledroid.com/github

不過須要注意的是不要上傳任何未發佈的產品web

ClassShark

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體積大,那麼咱們就着手瘦身了。

對APK進行瘦身

對資源進行極限壓縮

對資源進行極限壓縮,主要是如res裏面用到的圖片資源文件和assets的html,db等一些緩存預留在APK的數據文件

  • assets資源壓縮,使用7zip或者lzma壓縮方式最高
  • res 圖片資源的壓縮,使用tinypng優化Android的資源圖片,一般咱們能夠在保證圖片不失真的狀況下,多壓縮幾回。目前tinypng已經支持png和jpg圖片、.9圖的壓縮
  • 將非alpha的圖轉換成jpg形式

經過以上方法咱們圖片下降了79%的大小。

使用WEBP,SVG圖片資源格式

  • WebP是谷歌研發出來的一種圖片數據格式,它是一種支持有損壓縮和無損壓縮的圖片文件格式,若是應用支持到Android 4.0+,那麼咱們可使用WebP格式代替PNG,咱們的資源大小能下降50%多。或者有些資源可使用SVG圖片資源更小。
    如下是他們的對比圖:

這裏寫圖片描述

這裏提供方便轉換的WEBP資源的工具:

利用AndResGuard資源壓縮打包工具

微信中的資源混淆工具主要爲了混淆資源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 shrinkingProGuard結合使用。

  • ProGuard可以檢測和刪除未使用的類,字段,方法,和從你的打包應用程序的屬性,包括那些包含代碼庫,ProGuard是一個混淆優化字節碼的工具,可以刪除一些未使用的代碼,混淆使用的類,字段,方法和短名稱,通過混淆處理也可以使APK源代碼獲得保護

  • Code shrinking是一個Android Plugin for Gradle,從您的打包的應用程序中刪除未使用的資源,包括代碼庫中的未使用的資源。它工做在與代碼縮小,這樣,一旦未使用的代碼已被刪除,任何資源再也不引用能夠安全地刪除。

該功能須要依賴於:

  • SDK Tools 25.0.10 或更高
  • Android Plugin for Gradle 2.0.0 或更高

清除無用代碼

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/:

  • dump.txt
    描述.apk文件中全部類文件間的內部結構
  • mapping.txt
    列出了原始的類,方法和字段名與混淆後代碼間的映射。這個文件很重要,當你從release版本中收到一個bug報告時,能夠用它來翻譯被混淆的代碼。
  • seeds.txt
    列出了未被混淆的類和成員
  • usage.txt
    列出了從.apk中刪除的代碼
  • resources.txt
    列出resource被保留的資源
自定義混淆規則

在某些狀況下,默認的混淆器配置文件proguard-android.txt文件是會移除全部只有未使用的代碼,但也有可能會誤刪除了你須要的代碼,因此要注意如下幾種狀況:

  • AndroidManifest.xml配置的文件類
  • 使用了JNI 的接口方法
  • 運行時反射調用方法(不過如今ProGuard已經能夠處理這種了)

添加-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:keeptools: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 主要從如下位置合併資源:

  • src/main/res/ 主要資源
  • Gradle 使用variant(build type 和 build flavors)
  • Library的依賴使用

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.

那麼這種狀況咱們如何解決呢,能夠經過如下方法來清理:

  • 使用tools:discard,在 tools:shrinkMode=」strict」 的時候生效,指定某資源文件須要刪除。在你肯定該資源文件無效的時候使用。
  • 利用Lint找出未使用的資源並清理掉

    在Android Studio中打開「Analyze」 而後選擇」Inspect Code…」,範圍選擇整個項目,而後點擊」OK」

到這裏APK的大小又小了很多。

使用APK Splits構建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 只支持如下類型:

  • 屏幕密度
  • ABI

使用新的APK Splits,構建同一個應用程序的hdpi版本和mdpi版本,可以共享不少的任務 (如 javac,dx,proguard)。此外,它會被認爲是一個單一的variant,而且同一個測試程序將會被用來測試每​​個多APK。

按屏幕密度拆分

android {
  ...
  splits {
    density {
      enable true
      exclude "ldpi", "tvdpi", "xxxhdpi"
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
  • enable: 啓用屏幕密度拆分機制
  • exclude: 默認狀況下全部屏幕密度都包括在內,你能夠移除一些密度。
  • include: 表示要包括哪些屏幕密度
  • reset(): 重置屏幕密度列表爲只包含一個空字符串 (這可以實現,在與include一塊兒使用時能夠表示使用哪個屏幕密度,而不是要忽略哪一些屏幕密度)
  • compatibleScreens:表示兼容屏幕的列表。這將會注入到manifest中匹配的 節點。這個設置是可選的。

構建完成後能夠在out/apk/目錄下看到多個版本的APK

按 ABI 拆分

android {
  ...
  splits {
    abi {
      enable true
      reset()
      include 'x86', 'armeabi-v7a', 'mips'
      universalApk true
    }
  }
}
  • enable: 啓用ABI拆分機制
  • exclude: 默認狀況下全部ABI都包括在內,你能夠移除一些ABI。
  • include:指明要包含哪些ABI
  • reset():重置ABI列表爲只包含一個空字符串(這能夠實現,在與include一塊兒使用來能夠表示要使用哪個ABI,而不是要忽略哪一些ABI)
  • universalApk:指示是否打包一個通用版本(包含全部的ABI)。默認值爲 false。

例如:咱們項目主要提供xxhdpi的圖片資源,而項目中引用到了不少第三方庫(v七、v四、google Service 等Libarry資源)咱們沒法使用到,那麼咱們能夠經過這種方法來去除那些資源。這樣咱們的APK又減少了很是多。

使用多版本的APK

Multiple APK Support是一個在Google Play,能夠發佈不一樣的應用程序,分別針對不一樣的設備配置特徵。每一個APK是一個完整的、獨立的應用程序版本,但他們分享在Google Play相同的應用程序清單,必須共享相同的包名和與簽名。Google Play 會自動給你匹配相應的APK,這樣咱們的APK 就能夠是分不一樣版本構建須要資源文件,從而減少APK的大小。

經過發佈有多個APK,咱們能夠:

目前咱們基於這個方案作了不一樣屏幕的APK。

資源動態加載

咱們能夠在項目中使用資源動態加載形式,例如:表情,語言,離線庫等資源動態加載,減少APK的大小。

依賴庫優化

  • 若是應用支持的最低版本是API14,那就不要使用android support庫,或者分開使用android support庫,用哪一個引入哪一個庫(android-support-ui/android-support-core 等)雖然如今支持還不太好,Google servie 也是如此。
  • 使用更輕量級的庫代替,或者優化library的大小,否則本身寫更好。
  • 刪除armable-v7包下的so、刪除x86包下的so,基本上armable的so也是兼容armable-v7的,armable-v7a的庫會對圖形渲染方面有很大的改進,不過最好的是根據上面咱們說的提供多版本APK,對不一樣的平臺精簡,再或者動態的加載so。
  • 使用H5編寫界面,圖片雲端獲取
  • 資源緩存庫不放在assets下,雲端獲取更新。

支持插件化

將來對於一些獨立業務模塊,能夠作成插件化動態加載,用戶須要使用時,只需下載少部分插件。

使用ReDex優化

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

Android 之美 從0到1

相關文章
相關標籤/搜索