用户通常会避免下载看起来太大的应用,尤其是在新兴市场,这些市场的设备连接到不稳定的 2G 和 3G 网络,或者使用有限的数据套餐。此页面介绍如何减小应用的下载大小,让更多用户可以下载您的应用。
使用 Android 应用包上传您的应用
将您的应用作为Android 应用包上传,以便在发布到 Google Play 时立即减小应用大小。Android 应用包是一种上传格式,其中包含应用的所有已编译代码和资源,但将 APK 生成和签名推迟到 Google Play。
Google Play 的应用分发模型会使用您的应用包来为每个用户的设备配置生成和分发优化的 APK,以便他们只需下载运行您的应用所需的代码和资源。您无需构建、签名和管理多个 APK 来支持不同的设备,用户可以获得更小、更优化的下载。
对于使用应用包发布的应用,Google Play 会强制执行压缩下载大小限制,为 200 MB。使用 Play 功能交付和 Play 资源交付可以实现更大的大小,但是增加应用的大小可能会对安装成功率产生负面影响并增加卸载率,因此我们建议您应用本页中描述的指南,尽可能减小应用的下载大小。
了解 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 中包含的资源的数量和大小来减小 APK 的大小。特别是,您可以删除应用不再使用的资源,并可以使用可缩放的 Drawable
对象代替图像文件。本部分讨论这些方法以及您可以减少应用中资源以减小 APK 总大小的其他方法。
删除未使用的资源
lint
工具(Android Studio 中包含的静态代码分析器)会检测 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 插件 7.0 及更高版本中,您可以声明应用支持的配置。Gradle 使用 resourceConfigurations
风味和 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 对象
某些图像不需要静态图像资源。框架可以在运行时动态绘制图像。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 文件。- 要使用
aapt
工具优化图像文件,图像文件需要使用 256 种或更少的颜色。 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 文件格式
您可以使用WebP文件格式代替 PNG 或 JPEG 文件作为图像格式。WebP 格式提供有损压缩和透明度(如 JPG 和 PNG),并且它可以提供比 JPEG 或 PNG 更好的压缩效果。
您可以使用 Android Studio 将现有的 BMP、JPG、PNG 或静态 GIF 图像转换为 WebP 格式。有关更多信息,请参阅创建 WebP 图像。
使用矢量图形
您可以使用矢量图形创建分辨率无关的图标和其他可缩放媒体。这可以大幅减小APK的大小。矢量图像在Android中表示为VectorDrawable
对象。使用VectorDrawable
对象,一个100字节的文件可以生成与屏幕大小一样清晰的图像。
但是,系统渲染每个VectorDrawable
对象需要更多时间,较大的图像显示在屏幕上所需的时间更长。因此,仅在显示小图像时才考虑使用这些矢量图形。
有关使用VectorDrawable
对象的更多信息,请参阅Drawables。
将矢量图形用于动画图像
不要使用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 Bundles将您的应用上传到Google Play。上传应用捆绑包可以让Google Play为每个用户的设备配置生成和提供优化的APK,以便他们只下载运行您的应用所需的代码和资源。您无需构建、签名和管理多个APK来支持不同的设备,用户可以获得更小、更优化的下载。
如果您没有将应用发布到Google Play,您可以将应用分割成几个APK,这些APK的区别在于屏幕大小或GPU纹理支持等因素。
用户下载您的应用时,他们的设备会根据设备的功能和设置接收正确的APK。这样,设备就不会收到设备没有的功能的资源。例如,如果用户拥有hdpi
设备,则他们不需要您可能为更高密度显示屏的设备包含的xxxhdpi
资源。