縮減應用程式大小

使用者往往會避免下載看似大型的應用程式,這種現象在新興市場尤為明顯:在這些地方,使用者的裝置通常是連接 2G 和 3G 網路,而且連線狀態不穩定;或是依賴位元組付費計費方案。本頁將說明如何縮減您的應用程式下載大小,方便更多使用者下載。

使用 Android App Bundle 上傳應用程式

將應用程式上傳到 Google Play 最簡便的方式是利用 Android App Bundle 格式。這種全新的上傳格式包含應用程式所有已編譯的程式碼和資源,但在產生並簽署 APK 以發布到 Google Play 的過程中,會出現延遲現象。

上傳完畢後,Google Play 的新應用程式提供模型就會使用應用程式套件,根據各個使用者的裝置設定產生並提供經過最佳化的 APK,讓使用者只下載執行應用程式所需的程式碼和資源。您不再需要為不同裝置建構、簽署和管理多個 APK,而且使用者下載的內容不但檔案較小,還會經過最佳化處理。

提醒您,由於 Google Play 對透過應用程式套件發布的應用程式設有壓縮下載大小限制 (150 MB 以下),因此建議您遵守相關規範,盡可能減少應用程式的下載大小。

如果想藉由上傳簽署過的 APK,將應用程式發布至 Google Play,壓縮後的下載檔案大小不得超過 100 MB

瞭解 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:包含以 DEX 檔案格式編譯、且 Dalvik/ART 虛擬機器瞭解的類別。
  • 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 就會自動協助移除資源。

Groovy

android {
    // Other settings

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

Kotlin

android {
    // Other settings

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

您必須啟用程式碼縮減,才能使用 shrinkResources。在建構過程中,R8 會先移除未使用的程式碼。然後,Android Gradle 外掛程式會移除未使用的資源。

如要進一步瞭解程式碼和資源縮減,以及 Android Studio 如何協助您縮減 APK 大小,請參閱「縮小、模糊化及最佳化應用程式」一文。

在 Android Gradle 外掛程式 0.7 以上版本中,您可以宣告應用程式支援的設定。Gradle 使用 resConfigresConfigs 變種版本以及 defaultConfig 選項,將這些資訊傳遞至建構系統。接著,建構系統會防止其他不支援的設定在 APK 中顯示,進而縮減 APK 的大小。如要進一步瞭解這項功能,請參閱「移除未使用的額外資源」。

將程式庫資源使用降至最低

開發 Android 應用程式時,您通常會使用外部程式庫以提高應用程式的可用性和多功能性。舉例來說,您可以參考 Android 支援資料庫改善舊版裝置的使用者體驗,或者也可以使用 Google Play 服務擷取應用程式中文字的自動翻譯。

如果程式庫是專為伺服器或電腦所設計,其中可能包含許多應用程式不需要的物件和方法。如果只要包含應用程式所需的程式庫部分,授權允許修改程式庫即可編輯程式庫的檔案。另外,您也可以使用適合行動裝置的程式庫,為應用程式新增特定功能。

注意:程式碼縮減可清除程式庫中不必要的部分程式碼,但可能無法移除一些大型的內部依附元件。

原生動畫圖片解碼

在 Android 12 (API 級別 31) 中,NDK ImageDecoder API 已展開,可從使用動畫 GIF 和動畫 WebP 檔案格式的圖片解碼所有影格和時間資料。在 Android 11 中推出時,此 API 只會對這些格式動畫的第一個圖片進行解碼。

使用 ImageDecoder 取代第三方程式庫,進一步減少 APK 大小,並享有未來安全性和效能相關更新方面的優勢。

如要進一步瞭解 API,請參閱 API referenceGitHub 範例

僅支援特定密度

Android 支援非常大量的裝置,涵蓋多種不同的螢幕密度。在 Android 4.4 (API 級別 19) 以上版本中,此架構支援多種密度:ldpimdpitvdpihdpi, xhdpixxhdpixxxhdpi。雖然 Android 支援所有密度,但您不需要將光柵資產匯出至各個密度。

如果您確定只有一小部分使用者使用有特定密度的裝置,請考慮是否需要將這些密度納入應用程式。如果您沒有加入特定螢幕密度的資源,Android 會自動調整原先專為其他螢幕密度而設計的現有資源。

如果您的應用程式只使用經過調整的圖片,您可以在 drawable-nodpi/ 中使用單一圖片版本,這樣就能節省更多空間。建議每個應用程式都要至少包含一個 xxhdpi 圖片變化版本。

如要進一步瞭解螢幕密度,請參閱「螢幕大小和密度」一文。

使用可繪項目物件

部分圖片不需要使用靜態圖片資源;架構可以在執行階段動態繪製圖片。在 APK 中,Drawable 物件 (XML 中的 <shape>) 佔用的空間有限。除此之外,XML Drawable 物件還可產生符合質感設計指南的單色圖片。

重複使用資源

您可以針對圖片的變化版本加入個別資源,例如色調、濃淡或相同圖片的旋轉版本。不過我們建議重複使用相同的資源組合,並在執行階段視需求加以自訂。

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 檔案。如要避免這種情況發生,您可以使用 isCrunchPngs 標記停用 PNG 檔案的程序:

Groovy

buildTypes.all { isCrunchPngs = false }

Kotlin

buildTypes.all { isCrunchPngs = false }

壓縮 PNG 和 JPEG 檔案

您可以使用 pngcrushpngquantzopflipng 等工具縮減 PNG 檔案大小,同時不犧牲圖片品質。這些工具都能縮減 PNG 檔案的大小,同時維持良好的圖片品質。

pngcrush 工具特別有效率:這項工具會反覆套用 PNG 篩選器和 zlib (Deflate) 參數,並運用各個篩選器和參數組合壓縮圖片。然後再選用可產生最小壓縮輸出的設定。

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

使用 WebP 檔案格式

除了使用 PNG 或 JPEG 檔案之外,您也可以在指定 Android 3.2 (API 級別 13) 以上版本時,針對您的圖片使用 WebP 檔案格式。WebP 格式雖然有損壓縮 (例如 JPEG) 和透明度 (例如 PNG),但壓縮效果比 JPEG 或 PNG 更好。

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

使用向量圖形

您可以使用向量圖形,建立與解析度無關的圖示和其他可擴充的媒體。使用這些圖片可大幅減少 APK 大小。Android 系統中的向量圖片會以 VectorDrawable 物件的形式呈現。使用 VectorDrawable 物件時,100 位元組的檔案可以產生螢幕大小的清晰圖片。

但是,系統需要很長時間才能轉譯每個 VectorDrawable 物件,而較大的圖片需要較長的時間才會在螢幕中顯示。因此,建議您只在顯示小型圖片時才使用這些向量圖形。

如要進一步瞭解如何使用 VectorDrawable 物件,請參閱「使用可繪項目」一文。

動畫圖片使用向量圖形

請勿使用 AnimationDrawable 建立影格動畫,因為您必須為每個影格加入個別的點陣圖檔案,而這會大幅增加 APK 的尺寸。

請改用 AnimatedVectorDrawableCompat 建立 動畫向量可繪項目

減少原生和 Java 程式碼

您有多種方式可以減少應用程式中的 Java 和原生程式碼集大小。

移除不必要的產生程式碼

請務必瞭解所有自動產生的程式碼足跡。舉例來說,許多通訊協定緩衝區工具會產生過多的方法和類別,而這可能會導致應用程式大小增加一倍或三倍。

避免列舉

單一列舉項目可以在應用程式的 classes.dex 檔案中增加約 1.0 至 1.4 KB 的大小。對於複雜的系統或共用資料庫而言,這些新增項目可能會快速累積。如果可以,請考慮使用 @IntDef 註解和程式碼縮減以去除列舉,並將其轉換成整數。這種類型轉換可保留列舉的所有類型安全優勢。

縮小原生二進位檔

如果應用程式使用原生程式碼和 Android NDK,您也可以對程式碼進行最佳化,藉此縮減應用程式版本的大小。兩個實用的技巧是移除偵錯符號,以及不要擷取原生資料庫。

移除偵錯符號

如果應用程式仍處於開發階段,但仍需進行偵錯,那麼使用偵錯符號就很合理。使用 Android NDK 中提供的 arm-eabi-strip 工具,從原生資料庫移除不必要的偵錯符號。之後,您就可以編譯發布子版本。

避免擷取原生資料庫

建構應用程式版本時,請確認 APK 中的 useLegacyPackaging 已設為 false,藉此在 APK 中包裝未壓縮 .so 檔案應用程式的 build.gradle 檔案。停用此標記可防止 PackageManager 在安裝期間,從 APK 將 .so 檔案複製到檔案系統,並且還有縮小應用程式更新檔案大小的額外優勢。

維護多個精簡 APK

您的 APK 可能含有使用者下載但從未使用過的內容,例如其他語言或個別螢幕密度的資源。如要盡可能減少使用者下載的項目,建議您使用 Android App Bundle 將應用程式上傳至 Google Play。上傳應用程式套件後,Google Play 就會根據各使用者的裝置設定產生並提供經過最佳化的 APK,讓使用者只下載執行應用程式所需的程式碼和資源。您不再需要為不同裝置建構、簽署及管理多個 APK,而且使用者下載的內容不但檔案較小,還會經過最佳化處理。

如果未將應用程式發布至 Google Play,您可以將應用程式區隔成多個 APK,並依照螢幕大小或 GPU 材質支援等因素加以區別。

使用者下載應用程式後,裝置就會根據裝置的功能和設定接收正確的 APK。這樣一來,裝置就不會收到裝置沒有的功能相關資產。舉例來說,如果使用者使用的是 hdpi 裝置,就不需要加入適合高密度螢幕裝置使用的 xxxhdpi 資源。

詳情請參閱「設定 APK 分割」和「維護多個 APK」。