1. 程式人生 > >APK安裝包體積大小優化指南

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圖片版本。

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本身的體積,這裡就不過多介紹了

對第三方庫進行重新定製(重新打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了,減少了大量的無用依賴程式碼!