本篇文章翻譯自Reduce APK Size
使用者通常不會去下載體積過大的應用程式,特別是當自己的裝置連線的是 2G/3G 或者按位元組付費的網路。這篇文章描述瞭如何縮減 APK 的體積大小,以使得更多使用者願意下載你開發的應用。
瞭解APK結構
在討論如何縮減你應用的體積之前,瞭解 APK 結構是非常有益處的。一個 APK 檔案包含了一個 ZIP 檔案,該 ZIP 檔案包含了組成你應用的所有檔案,這些檔案包括 Java 位元組碼檔案、資原始檔和已編譯資源的檔案。 APK 包含下列目錄:
META-INF/
:包含了CERT.SF
、CERT.RSA
簽名檔案以及MAINFEST.MF
mainfest檔案assets/
:包含了應用程式的資源,應用程式可以通過 AssetManager 檢索資源res/
:包含了沒有編譯到resources.arsc
的資源lib/
:包含了特定處理器的軟體層的編譯程式碼,該目錄包含了每個平臺型別的子目錄,例如armeabi
、armeabi-v7a
、arm64-v8a
、x86
、x86_64
和mips
APK 也包含了下列檔案,在這些檔案之中,只有Mainfest.xml
是強制的
resources.arsc
:包含了編譯的資源,該檔案包含了來自res/values/
目錄下所有配置的XML內容。打包工具提取此XML內容,將其編譯為二進位制格式,並歸檔內容。此內容包含了語言字串和樣式,以及未直接包含在resources.arsc
檔案中的內容路徑,比如佈局檔案和影象。classes.dex
:包含了能被Dalvik/ART
虛擬機器識別的 DEX 檔案格式編譯的類AndroidManifest.xml
:包含了 Android 核心的 mainfest 檔案。該檔案羅列了應用程式的名字、版本、許可權和引用的第三方庫。該檔案使用 Android 的二進位制 XML 格式
減少資源數量和大小
APK 的大小會影響到應用程式啟動的速度、使用的記憶體和消耗的電量。縮減應用程式大小最簡單的方式之一就是減少它所包含的資源數量和大小。特別是你可以移除你的應用中不再使用的資源,或者使用可擴充的 Drawable 物件來替代影象檔案。這部分討論的這些方法以及一些其他的方式可以減少你應用程式中的資源從而在整體上減少 APK 體積的大小。
移除無用的資源
Android Studio中的靜態程式碼檢查工具——lint可以檢測res/
目錄下沒有引用的資源。當 lint 檢查工具發現在你專案中可能存在一個沒有使用的資源,它將會列印出類似如下的資訊:
res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
to be unused [UnusedResources]
複製程式碼
注意:lint 檢查工具沒有掃描
assets/
目錄,assets 資源是通過反射的方式,或者連結到應用程式的庫檔案來發現引用的。此外,lint 檢查工具並不刪除這些資源,它只是提醒你它們的存在。
你使用的一些庫可能包含了一些無用的資源,如果你在 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 應用的時候,通常會使用一些第三方庫來提高應用程式的可用性和多功能性。比如,可能使用了 Android Support Library 來改善在舊機型上的使用者體驗,或者使用 Google Play Services 為應用程式提供自動翻譯。 如果一個庫是為伺服器或者桌面設計的,那麼它通常包含了許多你的應用程式用不到的物件和方法,如果這個庫所使用的協議允許,那麼你可以修改這個庫檔案。當然,你也可以使用其他一些對於移動端友好的庫來為你的應用程式新增特定功能。
注意:ProGuard 可以清理第三方庫中對你應用非必須的程式碼,但是它不能移除第三方庫的大型內部依賴項。
只支援特定的解析度
Android 支援非常大的裝置集,擁有著各式各樣的解析度。在 Android4.4(API level 19)或者更高的系統版本,其框架支援許多解析度:ldpi
、mdpi
、tvdpi
、hdpi
、xhdpi
、xxhdpi
。儘管 Android 支援所有這些解析度,但是你並不需要適配每一種解析度。
如果你知道你的使用者群中只有一小部分使用具有特定解析度的裝置,考慮你是否需要適配這些解析度。如果你沒有為特定解析度準備資原始檔,那麼 Android 將自動縮放最初為其他螢幕解析度設計的現有資源。
如果你的應用程式只需要縮放的圖片,你可以通過在 drawable-nodpi
目錄中使用圖片的單個版本來節省更多的空間。我們建議每個應用程式至少包含一個xxhdpi
圖片版本。
更多有關螢幕解析度的資訊,可以檢視螢幕尺寸和密度
使用drawable物件
一些影象並不需要一個靜態的影象資源,frameworker 可以在執行時動態的繪製出影象。Drawable 物件佔用APK中少量空間。此外,XML形式的 Drawable 物件可以產生符合Material Design
準則的單色影象。
重用資源
你可以為影象的不同解析度新增一個單獨的資源,比如同一影象的著色、陰影或者旋轉版本。但是我們強烈建議你重用相同的資源集,在執行時根據需要定製它們。
Android 提供了幾個實用程式來更改 asset 的顏色,在 Android5.0(API level 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檔案 - aapt工具只能優化使用不多於256位顏色的影象檔案
- 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 level 13)或更高版本,除了使用 PNG 或 JPEG 格式的影象檔案,你還可以使用 WebP 檔案格式的影象。WebP 格式提供有失真壓縮(類似於JPEG)以及透明度(類似於PNG),但是能更好的提供比 JPEG 或 PNG 更好的壓縮效果。 你可以使用 Android Studio 轉換 BMP、JPG、PNG或者靜態GIF的影象為 WebP 格式。獲取更多資訊,檢視建立WebP影象
注意:Google Play 只接受啟動圖示為 PNG 格式的 APKs
使用向量圖形
你可以使用向量圖形建立獨立於解析度的圖示和其他可伸縮圖片。 使用這些圖形可以大大減少APK的大小。向量圖形在 Android 中表示為 VectorDrawable 物件。使用 VectorDrawable 物件,100位元組的檔案可以生成螢幕大小的清晰影象。 但是系統將會花費大量時間去渲染 VectorDrawable 物件,對於大的影象需要更長的時間才能出現在螢幕上。因此,在展示小影象的時候再考慮向量圖。 獲取更多有關 VectorDrawable 物件的資訊,檢視使用圖片
使用向量圖形制作動畫影象
不要使用 AnimationDrawable 去建立逐幀動畫,因為這樣做需要為動畫的每個幀新增單獨的點陣圖檔案,這會增加 APK 的體積大小。 你可以使用 AnimatedVectorDrawable 為向量圖片新增動畫
減少Native和Java程式碼
你可以使用下列幾種方式去減少應用程式中的 Java 和 native 程式碼庫。
移除不必要的生成程式碼
確保瞭解自動生成的程式碼的足跡。例如,許多協議緩衝工具生成過多的方法和類,這可能使應用程式的大小增加一倍或者兩倍。
避免列舉
一個列舉可以為你的應用程式的classes.dex
檔案增加1.0至1.4KB的大小,對於複雜系統或者共享庫,這些將快速累積。如果可能的話,考慮使用@IntDef
註解和 ProGuard 來除去列舉並將它們轉換為整數。這種型別轉換保留了列舉的所有型別安全的好處。
減小本地二進位制檔案的大小
如果你的應用程式使用 native code 和 Android NDK,你可以通過優化程式碼來減少應用程式的體積大小。兩種有用的技術是刪除除錯符號和避擴音取本地庫。
- 刪除除錯符號
如果你的應用程式正在開發並且需要除錯,那麼使用除錯符號是很有意義的。使用 Android NDK 提供的
arm-eabi-strip
工具去移除本地庫中不必要的除錯符號,之後再進行 release版本的編譯。 - 避擴音取本地庫
將
.so
檔案儲存在 APK 中未壓縮的檔案,設定application
中的元素android:extracNativeLibs
為false,這將防止 PackageManager 在安裝過程中將.so
檔案從APK複製到檔案系統,並且有一個額外的好處,使得應用程式的增量更新更小。
維護多個精簡版APK
你的 APK 可以包含使用者下載但從未使用的內容,例如區域或語言資訊。為了讓使用者下載儘量小的應用程式,你可以將你的應用程式根據螢幕尺寸或GPU紋理支援等因素細分為多個 APKs。
當使用者下載你的應用程式的時候,根據他們的裝置特點以及設定詳情,他們會接收到正確的 APK,這樣,裝置不會接收裝置沒有的功能的資源。例如,一個使用者有hdpi
的裝置,他們不需要為更高解析度的裝置提供的xxxhdpi
的資源。
獲取更多相關資訊,可以檢視 Configure APK Splits 和 Maintaining Multiple APKs