App 體積優化#
使用 Android App Bundle 上傳應用#
要在發布到 Google Play 時立即縮減應用大小,最簡單的方法就是將應用發布為 Android APP Bundle,這是一種全新的上傳格式,其中包含應用的所有經過編譯的代碼和資源,但 APK 生成及簽名則延後到 Google Play 商店來完成。
Google Play 的新應用提供模式名為 “Dynamic Delivery”,該機制使用您的 App Bundle 針對每位用戶的設備配置生成並提供經過優化的 APK,因此用戶只需下載運行您的應用所需的代碼和資源。您無需再編譯、簽署和管理多個 APK 以支持不同的設備,而用戶也可以獲得更小、更優化的下載文件包。
請注意,由於 Google Play 對使用 App Bundle 發布的應用強制執行不超過 150 MB 的壓縮下載大小限制,因此我們仍然建議您遵守本頁面介紹的指南,以盡可能減小應用的下載大小。
對於您通過上傳簽名的 APK 發布到 Google Play 的應用,其壓縮下載大小限制為不超過 100 MB。
了解 APK 結構#
在討論如何減小應用的大小之前,了解應用 APK 的結構會很有幫助。APK 文件由 Zip 壓縮文件(其中包含構成應用的所有文件)組成。這些文件包括 Java 類文件、資源文件和包含已編譯資源的文件。
APK 包含以下目錄:
-
META-INF/
:包含CERT.SF
和CERT.RSA
簽名文件,以及MANIFEST.MF
清單文件。 -
assets/
:包含應用的資源;應用可以使用 AssetManager 對象檢索這些資源。 -
res/
:包含未編譯到resources.arsc
中的資源。 -
lib/
:包含特定於處理器軟件層的編譯代碼。此目錄包含每種平台類型的子目錄,如armeabi
、armeabi-v7a
、arm64-v8a
、x86
、x86_64
和mips
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 { // Other settings buildTypes
{
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 會使用 resConfig
和 resConfigs
樣式以及 defaultConfig
選項將這些信息傳遞給編譯系統。隨後,編譯系統會阻止來自其他不受支持配置的資源出現在 APK 中,從而減小 APK 的大小。有關此功能的詳情,請參見移除未使用的備用資源。
最大限度減少庫中的資源使用#
在開發 Android 應用時,您通常需要使用外部庫來提高應用的可用性和多功能性。例如,您可以引用 Android 支持庫來提升舊設備上的用戶體驗,也可以使用 Google Play 服務檢索應用中文本的自動翻譯。
如果庫是為服務器或桌面設備設計的,則它可能包含應用不需要的許多對象和方法。要僅包含您的應用所需的庫部分,您可以編輯庫的文件(如果相應的許可允許您修改庫)。您還可以使用其他適合移動設備的庫來為應用添加特定功能。
注意:ProGuard 可以清理隨庫導入的一些不必要代碼,但它無法移除庫的大型內部依賴項。
僅支持特定密度#
Android 支持數量非常廣泛的設備(包含各種屏幕密度)。在 Android 4.4(API 級別 19)及更高版本中,框架支持各種密度:ldpi
、mdpi
、tvdpi
、hdpi,
、xhdpi
、xxhdpi
和 xxxhdpi
。儘管 Android 支持所有這些密度,但您無需將光柵化資源導出到每個密度。
如果您知道只有一小部分用戶擁有具有特定密度的設備,請考慮是否需要將這些密度捆綁到您的應用中。如果您不添加用於特定屏幕密度的資源,Android 會自動擴縮最初為其他屏幕密度設計的現有資源。
如果您的應用僅需要擴縮的圖片,則可以通過在 drawable-nodpi/
中使用圖片的單個變體來節省更多空間。我們建議每個應用至少包含一個 xxhdpi
圖片變體。
有關屏幕密度的詳情,請參見屏幕尺寸和密度。
使用可繪製對象#
某些圖片不需要靜態圖片資源;框架可以在運行時改為動態繪製圖片。Drawable 對象(XML 中為 <shape>
)會佔用 APK 中的少量空間。此外,XML Drawable 對象會生成符合 Material Design 規範的單色圖片。
重複使用資源#
您可以為圖片的變體添加單獨的資源,例如同一圖片經過色調調整、陰影設置或旋轉的版本。不過,我們建議您重複使用同一組資源,並在運行時根據需要對其進行自定義。
Android 提供了一些實用程序來更改資源的顏色,每個實用程序在 Android 5.0(API 級別 21)及更高版本上都使用 android:tint
和 tintMode
屬性。對於較低版本的平台,則使用 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 文件#
您可以使用 pngcrush、pngquant 或 zopflipng 等工具減小 PNG 文件的大小,同時不損失畫質。所有這些工具都可以減小 PNG 文件的大小,同時保持肉眼感知的畫質不變。
pngcrush
工具尤為有效:該工具會迭代 PNG 過濾器和 zlib (Deflate) 參數,使用過濾器和參數的每個組合來壓縮圖片。然後,它會選擇可產生最小壓縮輸出的配置。
要壓縮 JPEG 文件,您可以使用 packJPG 和 guetzli 等工具。
使用 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
工具從原生庫中移除不必要的調試符號。之後,您便可以編譯發布版本。
- 避免解壓縮原生庫
在編譯應用的發布版本時,您可以通過在應用清單的 元素中設置 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
資源。
減少 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 {...}
}