用户通常会避免下载看起来过大的应用,尤其是在新兴市场中,那里的设备可能连接到信号不稳定的 2G 和 3G 网络,或者使用有流量限制的套餐。本页面介绍了如何减小应用的下载大小,从而让更多用户能够下载您的应用。
使用 Android App Bundle 上传您的应用
将应用作为 Android App Bundle 上传,以便在发布到 Google Play 时立即节省应用大小。Android App Bundle 是一种上传格式,其中包含应用的所有已编译代码和资源,但将 APK 生成和签名推迟到 Google Play 进行。
Google Play 的应用分发模型会使用您的应用 bundle 为每位用户的设备配置生成并提供优化的 APK,以便用户仅下载运行应用所需的代码和资源。您无需构建、签名和管理多个 APK 来支持不同设备,用户也能获得更小、更优化的下载。
Google Play 对使用应用 bundle 发布的应用强制执行 200 MB 的压缩下载大小限制。使用 Play Feature Delivery 和 Play Asset Delivery 可以实现更大的大小,但增加应用大小可能会对安装成功率产生负面影响并增加卸载量,因此我们建议您应用本页面中描述的准则,以尽可能减小应用的下载大小。
了解 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 整体大小的方法。
移除未使用的资源
Android Studio 中包含的静态代码分析器 lint
工具会检测 res/
文件夹中您的代码未引用的资源。当 lint
工具在项目中发现潜在未使用的资源时,它会打印类似以下示例的消息:
res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources]
您添加到代码中的库可能包含未使用的资源。如果您在应用的 build.gradle.kts
文件中启用 shrinkResources
,Gradle 可以自动为您移除资源。
Kotlin
android { // Other settings. buildTypes { getByName("release") { minifyEnabled = true shrinkResources = true proguardFiles(getDefaultProguardFile('proguard-android.txt'), "proguard-rules.pro") } } }
Groovy
android { // Other settings. buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
要使用 shrinkResources
,请启用代码缩减。在构建过程中,R8 首先会移除未使用的代码。然后,Android Gradle 插件会移除未使用的资源。
有关代码和资源缩减以及 Android Studio 减小 APK 大小的其他方法的更多信息,请参阅缩减、混淆和优化应用。
在 Android Gradle Plugin 7.0 及更高版本中,您可以声明应用支持的配置。Gradle 使用 resourceConfigurations
variant 和 defaultConfig
选项将此信息传递给构建系统。然后,构建系统会阻止来自其他不受支持的配置的资源出现在 APK 中,从而减小 APK 的大小。有关此功能的更多信息,请参阅移除未使用的备用资源。
最大限度地减少库的资源使用
开发 Android 应用时,您通常会使用外部库来提升应用的可用性和多功能性。例如,您可以引用 AndroidX 以改进早期设备上的用户体验,或者您可以使用 Google Play 服务来检索应用内文本的自动翻译。
如果库是为服务器或桌面设计的,它可能包含应用不需要的许多对象和方法。要仅包含应用需要的库部分,如果许可允许您修改库,您可以编辑库的文件。您还可以使用替代的、适合移动设备的库来为应用添加特定功能。
原生动画图像解码
在 Android 12 (API 级别 31) 中,NDK ImageDecoder
API 得到扩展,可以解码使用动画 GIF 和动画 WebP 文件格式的图像的所有帧和时序数据。
使用 ImageDecoder
而不是第三方库,可以进一步减小 APK 大小,并受益于未来与安全性和性能相关的更新。
有关 ImageDecoder
API 的更多详细信息,请参阅API 参考
和 GitHub 上的示例。
仅支持特定密度
Android 支持不同的屏幕密度,例如:
ldpi
mdpi
tvdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
虽然 Android 支持上述密度,但您无需将栅格化资源导出到每种密度。
如果您知道只有一小部分用户使用具有特定密度的设备,请考虑是否需要将这些密度捆绑到您的应用中。如果您不包含特定屏幕密度的资源,Android 会自动缩放最初为其他屏幕密度设计的现有资源。
如果您的应用只需要缩放图片,您可以在 drawable-nodpi/
中只保留一种图片变体,从而节省更多空间。我们建议您在应用中至少包含一个 xxhdpi
图片变体。
有关屏幕密度的更多信息,请参阅屏幕尺寸和密度。
使用可绘制对象
某些图像不需要静态图像资源。框架可以在运行时动态绘制图像。 Drawable
对象(或 XML 中的 <shape>
)在 APK 中占用的空间很小。此外,XML Drawable
对象会生成符合 Material Design 指南的单色图像。
重用资源
您可以为图像的变体(例如同一图像的着色、阴影或旋转版本)包含单独的资源。但是,我们建议您重用同一组资源,并根据需要在运行时对其进行自定义。
Android 提供了多种实用程序来更改资源的颜色,可以使用 android:tint
和 tintMode
属性。
您还可以省略仅是其他资源的旋转等效项的资源。以下代码片段提供了一个示例,通过在图像中心旋转 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 文件禁用此过程:
Kotlin
buildTypes.all { isCrunchPngs = false }
Groovy
buildTypes.all { isCrunchPngs = false }
压缩 PNG 和 JPEG 文件
您可以使用 pngcrush、pngquant 或 zopflipng 等工具在不损失图像质量的情况下减小 PNG 文件大小。所有这些工具都可以在保持感知图像质量的同时减小 PNG 文件大小。
pngcrush
工具特别有效。此工具会遍历 PNG 过滤器和 zlib (Deflate) 参数,使用每个过滤器和参数组合来压缩图像。然后,它会选择产生最小压缩输出的配置。
要压缩 JPEG 文件,您可以使用 packJPG 和 guetzli 等工具。
使用 WebP 文件格式
除了使用 PNG 或 JPEG 文件,您还可以将 WebP 文件格式用于图像。WebP 格式提供有损压缩和透明度(类似于 JPG 和 PNG),并且可以提供比 JPEG 或 PNG 更好的压缩效果。
您可以使用 Android Studio 将现有的 BMP、JPG、PNG 或静态 GIF 图像转换为 WebP 格式。有关更多信息,请参阅创建 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
工具从原生库中移除不必要的调试符号。之后,您可以编译您的发布版本。
避免提取原生库
在构建应用的发布版本时,通过在应用的 build.gradle.kts
文件中将 useLegacyPackaging
设置为 false
,将未压缩的 .so
文件打包到 APK 中。禁用此标志可防止 PackageManager
在安装期间将 .so
文件从 APK 复制到文件系统。此方法使您的应用更新更小。
维护多个精简 APK
您的 APK 可能包含用户下载但从不使用的内容,例如额外的语言或每屏幕密度的资源。为帮助确保用户下载量最小化,请将您的应用使用 Android App Bundle 上传到 Google Play。上传应用 bundle 可让 Google Play 为每位用户的设备配置生成并提供优化的 APK,以便他们仅下载运行应用所需的代码和资源。您无需构建、签名和管理多个 APK 来支持不同设备,用户也能获得更小、更优化的下载。
如果您不将应用发布到 Google Play,您可以将应用划分为多个 APK,通过屏幕尺寸或 GPU 纹理支持等因素进行区分。
当用户下载您的应用时,他们的设备会根据设备的特性和设置接收正确的 APK。这样,设备就不会收到其不具备的功能的资源。例如,如果用户有 hdpi
设备,他们就不需要您可能为更高密度显示器设备包含的 xxxhdpi
资源。