APK安裝包體積大小優化指南

前言

    本文旨在介紹常見的優化APK體積大小的常用方法,截圖介紹多轉至別人博客,我只稍微整理。

APK構成

    在討論如何減小apk大小之前,理解apk的結構很有必要。一個APK文件包括一個ZIP 文件,該ZIP包含app的所有文件。包括java 字節碼文件,資源文件和一個包含了編譯後的資源文件。APK包含以下目錄:

  • META-INF/:包含了CERT.SFCERT.RSA 簽名文件, 以及 MANIFEST.MFmanifest 文件.
  • assets/: 包含了app的assets,app可以通過 AssetManager 對象獲取到這些資源
  • res/: 包含了那些沒有被編譯到 resources.arsc的資源
  • lib/: 包含了用於軟件處理器的編譯代碼,該目錄包含一個子目錄,針對不同平臺: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips.

一個APK也包含了下面的文件,但只有 AndroidManifest.xml 是強制性的

  • resources.arsc:

    包含了編譯後的資源。該文件包含了 res/values/文件夾下的所有XML內容。打包工具抽取了XML內容,將它編譯成二進制格式,並且進行了壓縮。該內容包括language strings和styles,以及未直接包含在resources.arsc 文件中的內容路徑。比如layout文件和圖片。

  • classes.dex:

    包含可以被Dalvik/ART 識別,以dex文件格式編譯後的代碼

  • AndroidManifest.xml:

    包含了Android核心mainfest文件。該文件羅列了app名字,版本,訪問權限,和引用的library文件。該文件採用二進制XML格式。

使用APK Analyser 分解你的 APK

    Android Studio 提供了一個有用的工具:APK Analyser。APK Analyser 將會拆解你的應用並讓你知道 .apk 文件中的那個部分佔據了大量空間。進入Android Studio的菜單中選擇Analyze->Inspecting Code即可,讓我們看一下在沒有經過優化之前的截圖:

這裏寫圖片描述

從 Apk Analyser 的輸出來看,應用的原大小是 24.4MB。經過 Play 商店的壓縮,大致是 23.3MB。

從截圖中可以看出主要有 3 個文件夾佔據了應用的大多數空間。

  • lib — 用來存放一些用到第三方的jar包和so文件
  • classes.dex — 這是 dex 文件,包含了所有會運行在你的 DVM 或 ART 裏的字節碼文件。
  • res — 這個文件夾包含了所有在 res 文件夾下的文件。大部分情況下它包含所有圖片,圖標和源文件,菜單文件和佈局。

這裏寫圖片描述

減小 classes.dex

    classes.dex 包含了所有 Java 代碼。當你編譯你的應用時,gradle 會將你的所有模塊裏的 .class 文件轉換成 .dex 文件並將這些文件合成一個 classes.dex 文件。

    單個的 classes.dex 文件可以容納大約 64K 方法。如果你達到了這個限制,你必須要在你的工程中啓用 multidexing。這將會創建另一個 classes1.dex 文件去存儲剩下的方法。所以 classes.dex 文件數目由你的方法數而定。

這裏寫圖片描述

你可以看到現在的我的應用包含 7548 個類和 52763 個方法。這個結果是沒有經過混淆的。你有兩個默認的混淆文件。

  • proguard-android.txt
  • proguard-rules.pro
release {
    //Enable the proguard
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), "proguard-rules.pro"

    //...
}

    就像文件名寫的那樣,“proguard-android.txt”是更積極的混淆選項。我們將這個作爲默認的混淆配置。你可以在 /app 目錄下的 proguard-rules.pro 裏添加自定義的混淆配置。

    proguard-android.txt這個默認混淆配置位置是在:sdk目錄下 ~sdk\tools\proguard\proguard-android.txt

    通過設置 minifyEnabled 爲 true,混淆將會移除所有未使用的方法、指令以減小 classes.dex 文件。

這是啓用了 minify 之後的 APK:
這裏寫圖片描述

減小 res

    下一大塊就是 res 文件夾,它包括了所有的圖片,raw 文件和 XML。你不能添加/刪除/修改你的 XML,因爲它們包含了你的佈局。但是我們可以減小圖片文件。

  • “shrinkResources” 屬性將會移除所有在工程中沒有用到的資源。在 build.gradle 中像下面這樣啓用它:
release{
  //...
  //...
  shrinkResources true
  //...
}
  • “resConfigs” 屬性將會在構建過程中移除所有本地化資源。添加下面的這些代碼讓應用只支持中文。

defaultConfig {
    //...
    //...
    //...

    //strip other than english resources
    resConfigs "zh"
}
  • 你可以使用 webp 替代 png

    如果你在用 Android Studio 2.3,並且你的應用的最低支持版本大於 18,你可以使用 webp 替代 png。webp 圖片比 png 體積更小但質量一樣。而且 Android 支持 webp。所以你可以在 ImageView 中像加載其它光柵圖片一樣加載 webp 圖片。這不需要改變你的佈局。

    你可以在工程選擇 drawable 和 mipmap 文件夾,右擊並選擇 convert to webp。這將會打開下面這樣的配置彈框。

這裏寫圖片描述

    點擊 ok,將會將所有 png 圖片轉成 webp。如果 webp 圖片比 png 更大,Android Studio 將會自動跳過這個文件。

這裏寫圖片描述

只支持特定的分辨率

Android支持非常大的設備集,包括各種屏幕密度。 在Android 4.4(API級別19)及更高版本中,框架支持各種分辨率:ldpi,mdpi,tvdpi,hdpi,xhdpi,xxhdpi和xxxhdpi。 雖然Android支持所有這些分辨率,但你不需要導出光柵化資源到每種分辨率。

如果你知道只有一小部分用戶使用特定分辨率的設備,請考慮是否需要支持這些分辨率。 如果你不包括特定屏幕密度的資源,Android會自動縮放最初爲其他屏幕密度設計的現有資源。

如果您的應用只需要縮放的圖片,您可以通過在drawable-nodpi /中使用圖片的單個版本來節省更多空間。 我們建議每個應用至少包含一個xxhdpi圖片版本。

更多屏幕分辨率的信息,可以參考 Screen Sizes and Densities.

so的優化

我們在接入百度地圖的時候,發現需要引入很多很多so

這些so文件佔了很多大體積,如果你不加控制,所有的so都會打包到你的apk了,最後發現這些so文件盡然佔了我們apk的近乎三分之一的體積。然而考慮下我們的用戶,基本都是跑在手機上的(沒有人跑在模擬器上),所以明顯x86和x86_64的so是不需要支持,那麼我們可以通過配置gradle來制定只打包某些so,依然是在defualtConfig中:

複製代碼
defaultConfig {
        
        ... ...

        ndk {
            //設置支持的SO庫架構
            abiFilters 'arm64-v8a', 'armeabi' //, 'x86', , 'x86_64', 'arm64-v8a'
        }
    }
複製代碼

最後打包出來的apk真的是減少了好幾個MB,這是太好了

 

當然如果有一些so是你們自己開發的,那麼可以參考這個文章來參考如果用ndk開發的時候減少so本身的體積,這裏就不過多介紹了

https://blog.algolia.com/android-ndk-how-to-reduce-libs-size/

對第三方庫進行重新定製(重新打jar包)

開發中引入大量的第三方開發庫也是一個增加apk體積的重要原因,因爲你把人家的代碼和資源全給包含進來了。但是想想人家的代碼,並不一定全要的,是否可以只引入人家的一部分代碼,而不是在build.gradle中僅僅添加一行“compile”來全部依賴呢?答案是可以得!這裏舉一個例子

我們開發中有一個需求是將數據通過圖標的方式顯示出來,這裏我們站在巨人的肩膀上,使用了MPAndroidChart這個優秀的開源項目(https://github.com/PhilJay/MPAndroidChart),但是發現他們的東西太多了,我們僅僅需要使用其中一種chart。如果在build.gradle裏面加一句:

dependencies {
    compile 'com.github.PhilJay:MPAndroidChart:v2.2.3'
}

這要把他們的庫全給引用過來了。想到他們是開源的,代碼有,所以我們僅僅把他們的我們所用到的代碼給剝離出來,單獨打包了一個jar包引入到我們的項目裏面,就OK了,減少了大量的無用依賴代碼!




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章