banner
fwrite

fwrite

好好生活
twitter
github
email

APK 瘦身

APK 結構#

在討論如何減小應用的大小之前,了解應用 APK 的結構會很有幫助。APK 檔案由 Zip 壓縮檔(其中包含構成應用的所有檔案)組成。這些檔案包括 Java 類檔案、資源檔案和包含已編譯資源的檔案。

APK 包含以下目錄:

  • META-INF/:包含 CERT.SFCERT.RSA 簽名檔,以及 MANIFEST.MF 清單檔。
  • assets/:包含應用的資源;應用可以使用 AssetManager 物件檢索這些資源。
  • res/:包含未編譯到 resources.arsc 中的資源。
  • lib/:包含特定於處理器軟體層的編譯代碼。此目錄包含每種平台類型的子目錄,如 armeabiarmeabi-v7aarm64-v8ax86x86_64mips

APK 還包含以下檔案。在這些檔案中,只有 AndroidManifest.xml 是必需的。

  • resources.arsc:包含已編譯的資源。此檔案包含 res/values/ 資料夾的所有配置中的 XML 內容。打包工具會提取此 XML 內容,將其編譯為二進位檔形式,並將相應內容進行歸檔。此內容包括語言字串和樣式,以及未直接包含在 resources.arsc 檔案中的內容(例如佈局檔案和圖片)的路徑。
  • classes.dex:包含以 Dalvik/ART 虛擬機可理解的 DEX 檔案格式編譯的類。
  • AndroidManifest.xml:包含核心 Android 清單檔。此檔案列出了應用的名稱、版本、訪問權限和引用的庫檔案。該檔案使用 Android 的二進位 XML 格式。

減少資源數量和大小#

APK 的大小會影響應用加載速度、使用的記憶體量以及消耗的電量。減小 APK 大小的一種簡單方法是減少其包含的資源數量和大小。具體來說,您可以移除應用不再使用的資源,並且可以用可伸縮的 Drawable 物件取代圖片檔案。此部分將討論上述這些方法,以及另外幾種可減少應用中的資源以減小 APK 總大小的方法。

移除未使用的資源#

lint 工具是 Android Studio 中附帶的靜態代碼分析器,可檢測到 res/ 資料夾中未被代碼引用的資源。當 lint 工具發現專案中有可能未使用的資源時,會顯示一條消息,如下例所示。

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources]

注意lint 工具不會掃描 assets/ 資料夾、通過反射引用的資源或已鏈接到應用的庫檔案。此外,它也不會移除資源,只會提醒您它們的存在。

您添加到代碼的庫可能包含未使用的資源。如果您在應用的 build.gradle 檔案中啟用了 shrinkResources,則 Gradle 可以代表您自動移除資源。

    android { 
        { 
            release { 
                minifyEnabled true 
                shrinkResources true 
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
            } 
        } 
    }

要使用 shrinkResources,您必須啟用代碼壓縮功能。在編譯過程中,首先,ProGuard 會移除未使用的代碼,但會保留未使用的資源。然後,Gradle 會移除未使用的資源。

要詳細了解 ProGuard 以及 Android Studio 幫助您減小 APK 大小的其他方式,請參見 壓縮代碼和資源

在 Android Gradle Plugin 0.7 及更高版本中,您可以聲明應用支持的配置。Gradle 會使用 resConfigresConfigs 風格以及 defaultConfig 選項將這些信息傳遞給編譯系統。隨後,編譯系統會阻止來自其他不受支持配置的資源出現在 APK 中,從而減小 APK 的大小。有關此功能的詳情,請參見 移除未使用的備用資源

減少庫中的資源使用#

在開發 Android 應用時,您通常需要使用外部庫來提高應用的可用性和多功能性。例如,您可以引用 Android 支持庫 來提升舊設備上的用戶體驗,也可以使用 Google Play 服務 檢索應用中文本的自動翻譯。

如果庫是為伺服器或桌面設備設計的,則它可能包含應用不需要的許多物件和方法。要僅包含您的應用所需的庫部分,您可以編輯庫的檔案(如果相應的許可允許您修改庫)。您還可以使用其他適合移動設備的庫來為應用添加特定功能。

注意ProGuard 可以清理隨庫導入的一些不必要代碼,但它無法移除庫的大型內部依賴項。

僅支持特定密度#

Android 支持數量非常廣泛的設備(包含各種螢幕密度)。在 Android 4.4(API 級別 19)及更高版本中,框架支持各種密度:ldpimdpitvdpihdpi,xhdpixxhdpixxxhdpi。儘管 Android 支持所有這些密度,但您無需將光柵化資源導出到每個密度。

如果您知道只有一小部分用戶擁有具有特定密度的設備,請考慮是否需要將這些密度捆綁到您的應用中。如果您不添加用於特定螢幕密度的資源,Android 會自動擴縮最初為其他螢幕密度設計的現有資源。

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

有關螢幕密度的詳情,請參見 螢幕尺寸和密度

使用可繪製物件#

某些圖片不需要靜態圖片資源;框架可以在運行時改為動態繪製圖片。Drawable 物件(XML 中為 <shape>)會佔用 APK 中的少量空間。此外,XML Drawable 物件會生成符合 Material Design 準則的單色圖片。

重複使用資源#

您可以為圖片的變體添加單獨的資源,例如同一圖片經過色調調整、陰影設置或旋轉的版本。不過,我們建議您重複使用同一組資源,並在運行時根據需要對其進行自定義。

Android 提供了一些實用程式來更改資源的顏色,每個實用程式在 Android 5.0(API 級別 21)及更高版本上都使用 android:tinttintMode 屬性。對於較低版本的平台,則使用 ColorFilter 類。

您還可以省略僅是另一資源的旋轉等效項的資源。以下代碼段提供了一個示例,展示了通過繞圖片中心位置旋轉 180 度,將 “拇指向上” 變為 “拇指向下”:

    <?xml version="1.0" encoding="utf-8"?> 
    <rotate xmlns:android="http://schemas.android.com/apk/res/android" 
    		android:drawable="@drawable/ic_thumb_up" 
    		android:pivotX="50%" 
    		android:pivotY="50%" 
    		android:fromDegrees="180" />

從代碼進行渲染#

您還可以通過按一定程序渲染圖片來減小 APK 大小。按一定程序渲染可以釋放空間,因為您不再在 APK 中存儲圖片檔案。

壓縮 PNG 檔案#

aapt 工具可以在編譯過程中通過無損壓縮來優化放置在 res/drawable/ 中的圖片資源。例如,aapt 工具可以通過調色板將不需要超過 256 種顏色的真彩色 PNG 轉換為 8 位 PNG。這樣做會生成質量相同但記憶體佔用量更小的圖片。

請記住,aapt 具有以下限制:

  • aapt 工具不會壓縮 asset/ 資料夾中包含的 PNG 檔案。

  • 圖片檔案需要使用 256 種或更少的顏色才可供 aapt 工具進行優化。

  • aapt 工具可能會增大已壓縮 PNG 檔案。為防止出現這種情況,您可以使用 Gradle 中的 cruncherEnabled 標記為 PNG 檔案停用此過程:

    aaptOptions { cruncherEnabled = false }

壓縮 PNG 和 JPEG 檔案#

您可以使用 pngcrushpngquantzopflipng 等工具減小 PNG 檔案的大小,同時不損失畫質。所有這些工具都可以減小 PNG 檔案的大小,同時保持肉眼感知的畫質不變。

pngcrush 工具尤為有效:該工具會迭代 PNG 過濾器和 zlib (Deflate) 參數,使用過濾器和參數的每個組合來壓縮圖片。然後,它會選擇可產生最小壓縮輸出的配置。

要壓縮 JPEG 檔案,您可以使用 packJPGguetzli 等工具。

使用 WebP 檔案格式#

如果以 Android 3.2(API 級別 13)及更高版本為目標,您還可以使用 WebP 檔案格式的圖片(而不是使用 PNG 或 JPEG 檔案)。WebP 格式提供有損壓縮(如 JPEG)以及透明度(如 PNG),不過與 JPEG 或 PNG 相比,這種格式可以提供更好的壓縮效果。

您可以使用 Android Studio 將現有 BMP、JPG、PNG 或靜態 GIF 圖片轉換為 WebP 格式。有關詳情,請參見 使用 Android Studio 創建 WebP 圖片

注意:僅當 啟動器圖標 使用 PNG 格式時,Google Play 才會接受 APK。

使用矢量圖形#

您可以使用矢量圖形創建與解析度無關的圖標和其他可伸縮媒體。使用這些圖形可以極大地減少 APK 占用的空間。矢量圖片在 Android 中以 VectorDrawable 物件的形式表示。借助 VectorDrawable 物件,100 字節的檔案可以生成與螢幕大小相同的清晰圖片。

不過,系統渲染每個 VectorDrawable 物件需要花費大量時間,而較大的圖片則需要更長的時間才能顯示在螢幕上。因此,請考慮僅在顯示小圖片時使用這些矢量圖形。

有關使用 VectorDrawable 物件的詳情,請參見 使用可繪製資源

將矢量圖形用於動畫圖片

請勿使用 AnimationDrawable 創建逐幀動畫,因為這樣做需要為動畫的每個幀添加單獨的位圖檔案,而這會大大增加 APK 的大小。

您應改為使用 AnimatedVectorDrawableCompat 創建 動畫矢量可繪製資源

減少原生和 Java 代碼#

您可以使用多種方法來減小應用中 Java 和原生代碼庫的大小。

移除不必要的生成代碼

確保了解自動生成的任何代碼所佔用的空間。例如,許多協議緩衝區工具會生成過多的方法和類,這可能會使應用的大小增加一倍或兩倍。

避免使用枚舉

單個枚舉會使應用的 classes.dex 檔案增加大約 1.0 到 1.4 KB 的大小。這些增加的大小會快速累積,產生複雜的系統或共享庫。如果可能,請考慮使用 @IntDef 註解和 ProGuard 移除枚舉並將它們轉換為整數。此類型轉換可保留枚舉的各種安全優勢。

減小原生二進位檔的大小

如果您的應用使用原生代碼和 Android NDK,您還可以通過優化代碼來減小發佈版本應用的大小。移除調試符號和不提取原生庫是兩項很實用的技術。

移除調試符號#

如果應用正在開發中且仍需要調試,則使用調試符號非常合適。您可以使用 Android NDK 中提供的 arm-eabi-strip 工具從原生庫中移除不必要的調試符號。之後,您便可以編譯發佈版本。

避免解壓縮原生庫#

在編譯應用的發佈版本時,您可以通過在應用清單的 application 元素中設置 android:extractNativeLibs="false",打包 APK 中未壓縮的 .so 檔案。停用此標記可防止 PackageManager 在安裝過程中將 .so 檔案從 APK 複製到檔案系統,並具有減小應用更新的額外好處。

維持多個精簡 APK#

APK 可能包含用戶下載但從不使用的內容,例如其他語言或針對螢幕密度的資源。要確保為用戶提供最小的下載檔案,您應該 使用 Android App Bundle 將應用上傳到 Google Play。通過上傳 App Bundle,Google Play 能夠針對每位用戶的設備配置生成並提供經過優化的 APK,因此用戶只需下載運行您的應用所需的代碼和資源。您無需再編譯、簽署和管理多個 APK 以支持不同的設備,而用戶也可以獲得更小、更優化的下載檔案包。

如果您不打算將應用發佈到 Google Play,則可以將應用細分為多個 APK,並按螢幕尺寸或 GPU 紋理支持等因素進行區分。

當用戶下載您的應用時,他們的設備會根據設備的功能和設置接收正確的 APK。這樣,設備不會接收用於設備所不具備的功能的資源。例如,如果用戶具有 hdpi 設備,則不需要您可能會為具有更高密度顯示器的設備提供的 xxxhdpi 資源。

有關詳情,請參見 配置 APK 拆分維持多個 APK

減少 Native 體積#

android {
    ...
    defaultConfig {
        ...
        //在 defaultConfig 節點下配置 externalNativeBuild 塊
        externalNativeBuild {
            // For ndk-build, instead use ndkBuild {}
            cmake { //指定 cmake 的編譯選項
                // Sets optional flags for the C compiler.
                cFlags "-fvisibility=hidden  -ffunction-sections -fdata-sections"
                // Sets optional flags for the C++ compiler.
                cppFlags "-fvisibility=hidden  -ffunction-sections -fdata-sections -std=c++11"

                arguments "-DANDROID_TOOLCHAIN=gcc"//設置使用 gcc 編譯,cmake 默認使用 clang 編譯
            }
        }
    }

    buildTypes {...}
  
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。