首页 > 代码库 > 缩小apk的大小

缩小apk的大小

原文地址:https://developer.android.com/topic/performance/reduce-apk-size.html

减小APK的体积

This lesson teaches you to

  1. Understand the APK Structure
  2. Reduce Resource Count and Size
  3. Reduce Native and Java Code
  4. Maintain Multiple Lean APKs

You should also read

  • Shrink Your Code and Resources

如果你的app看起来很大,用户通常避免下载,特别是在很多新兴的国家,人们使用2G和3G网络接入因特网,和按流量计费。这篇文章介绍如何减小app的体积,这会帮助更多的用户来下载你的app。

理解apk的结构


在讨论如何减小你的app的apk大小之前,理解一个apk的结构是很有帮助的。一个apk文件,是一个zip文件,包含java class文件,资源文件,和编译的文件资源。

一个apk包含下列文件:

  • META-INF/: 包含 CERT.SF 和CERT.RSA 签名文件, 也包括 MANIFEST.MF 配置文件.
  • assets/: 包含一个app的引用资源文件,用AssetManager 检索
  • res/: 包含那些没有被编译到 resources.arsc 的资源.
  • lib/: 编译好的代码。有子文件,代表平台类型,如armeabiarmeabi-v7aarm64-v8ax86x86_64, and mips.

一个apk也包含下面这些文件。 下面只有 AndroidManifest.xml 必须会有.

  • resources.arsc: 包含编译的资源文件,主要是res/values/下面的XML文件。打包工具从xml里面提炼内容,编译成二进制的形式。这些内容包括字符,样式,也包含那些不在resources.arsc里面的文件,如图片和布局文件的路径。
  • classes.dex: 包含被虚拟机Dalvik和ART识别的,以Dex格式存在的classes编译文件。
  • AndroidManifest.xml: android核心配置文件。配置文件列出了app的名称,版本,权利,和相关的包。xml格式。

减少资源的数量和体积


apk体积的大小,对app加载的速度,占用的内存,消耗的电力有很大的影响。让apk变小的,很常见很有效的一点是,减少资源的数量和体积。特别是,减少app不再使用的资源,使用放大的Drawable对象代替图片文件。这节讨论几种方法,较少app的资源,来减少app超出的体积。

减少无用的资源

lint工具, 一个android studio的静态代码分析器,删除你的res/文件中代码不再引用的资源.当lint工具 在工程中发现一部分未使用的资源,会打印下列信息.

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
    to be unused [UnusedResources]

Note: lint 工具不扫描assets/文件, assets that are referenced via reflection, or library files that you‘ve linked to your app. Also, it doesn‘t remove resources; it only alerts you to their presence.

Libraries 你添加的lib可能包含没有用的资源. 在 app的build.gradle 文件添加 shrinkResources == true 时, Gradle 会自动减少这些资源.

android {
    // Other settings

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

用这个shrinkResources, 你必须确保你的代码shrinking. 在编译过程中,第一个 ProGuard 属性会删除无用的code , 但是会留下无用的资源。然后,Gradle 会删除这些无用的资源.

要了解 ProGuard 更多信息,和其他的android studio帮助减小apk大小的方法,请看Shrink Your Code and Resources.

在android gradle 0.7 及以上版本,你可以申明你app支持的配置。Gradle使用这些选项【resConfig and resConfigs flavors and the defaultConfig option】来通过编译系统。编译系统可以防止不受配置支持的资源出现在apk文件,以此来减小apk的大小。此功能的更多信息,请看Remove unused alternative resources.

使用最小化资源的库

当我们开发android app,通常会使用第三方的库来实现功能和效率. 例如,你可能用到Android Support Library来改善在旧设备上的用户体验, 或者你可以使用Google Play Services实现app里面的文字的自动翻译。

如果一个库是为了服务器,和电脑设计,它可能包含很多app不需要的对象。如果许可修改这个库,你应该编辑这个库,以仅仅包含app需要使用的部分。你还可以使用手机友好的库加入你的app,作为一种替换方法。

Note: ProGuard can clean up some unnecessary code imported with a library, but it can‘t remove a library‘s large internal dependencies.

只支持特定的密度

Android支持多种密度的设备。在Android 4.4 (api level 19)及以上版本, the framework 支持多种密度:ldpi,mdpi,tvdpi,hdpi,xhdpi,xxhdpi and xxxhdpi. 尽管Android支持所有这些密度,你没有必要为每种密度导出资源。

如果你知道某种密度的用户比较少,考虑是否需要这些密度打包到你的app。如果你不包含这种密度的资源,android会自动将现有的其他密度的资源通过缩放显示. 

如果你的app之需要缩放的图片,你会通过拥有一中drawable-nodpi/图片来节省更多的空间。我们确保每个app都只会扫包含xxhdpi这种。

更多屏幕密度,请看 Screen Sizes and Densities.

减少动画帧

逐帧动画,会非常明显的增加apk的大小。图1显示了一个逐帧动画的例子,在一个文件夹中包含多个不同的PNG。每张图片就是动画的一帧。

你添加一帧,就在apk里多一张图片。图1,这个图片动画大概每秒30张左右。如果用每秒15张代替,这个动画仅需要一般的帧数。


技术分享


Figure 1. Frame by frame animations stored as resources.

使用Drawable对象

有写图片,不需要一个静态的图片资源。framework可以在运行时,动态地画出图片。Drawable 对象(<shape> in XML) 会占用apk极少的空间. 另外, XML Drawable 对象 会生成如何 material design 原则的单色图片。

复用资源

通一张图片的多种形式,如着色,阴影,旋转等,你可以使用一张单独的资源.然而,我们建议你重用相同的一组资源,在运行时根据需要定制它们。

Androdi提供多种工具来改变组件的颜色,5.0或更高的通过 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_arrow_expand"
    android:fromDegrees="180"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="180" />

代码渲染

你也可以通过程序渲染图片来减少apk的大小。程序渲染可以释放空间,因为i不需要在apk里面存储。

紧缩PNG文件

aapt工具可以在编译过程中,无损地优化res/drawable/的图片资源。例如,aapt 工具 通过一个调色板 可以把一个真彩色的PNG转变成一个8bit的PNG,不再需要256颜色。这意味同等质量,但更小的内存占用.

记住 aapt 有以下局限:

  • aapt 工具不能缩小asset/下面的 PNG
  • aapt工具缩小图片文件,需要它们use 256或更少颜色
  • aapt工具可能会加压已经压缩的PNG图片。为了避免这一点,你要是在Gradle中使用cruncherEnabled禁止PNG文件的这个过程。
aaptOptions {
    cruncherEnabled = false
}

压缩PNG and JPEG 文件

你可以使用pngcrushpngquant, or zopflipn等工具,在不损失图片质量的情况下,减少PNG的文件大小。所有这些工具都可以在保护图片质量的情况下,减少PNG文件的大小。


pngcrush工具尤其有效:这个工具遍历PNG过滤器和zlib(缩小)参数,使用过滤器和参数的组合压缩图像。然后选择配置收益率最小的压缩输出。


对于JPEG文件,您可以使用packJPG工具,它可以压缩JPEG文件成一个紧凑的形式。


使用WebP文件格式

替换PNG和JPEG文件,你可以使用WebP文件格式。WebP 格式兼有 JPEG的有损压缩,和 PNG的透明度,但是比单独的JPEG或PNG更好的压缩。

使用WebP有一些明显的缺陷。第一,android 3.2 以下不支持。第二,和PNG相比,系统解码该格式的图片需要更多的时间。

Note: 谷歌市场只接收app启动图标为PNG的apk。不支持其他格式的图片作为启动icon。

你可以使用 Android Studio 将现有的 BMP, JPG, PNG or GIF 图片换成WebP格式.更多信息,请看 Create WebP Images Using Android Studio.

使用向量图片

你可以使用矢量图形 创建分辨率无关的图标和其他可伸缩的媒体。使用这些图形可以大大减少你的APK大小。矢量图在在Android 中用VectorDrawable对象表示VectorDrawable对象,一个100字节的文件可以生成一个锋利的屏幕图像的大小。


然而,系统呈现每个VectorDrawable对象需要大量的时间,显示到屏幕的图像更大,花费的时间更长因此,只有当显示小图像,考虑使用这些矢量图形

For more information on working with VectorDrawable objects, see Working with Drawables.

减少本地和java代码


有下面几种方法.

移除产生的不必要的代码

确保明白自动产生的任何代码的footprint。譬如,很多protocol buffer工具会产生过多的方法,会成倍,甚至三倍地增加代码大小。

移除枚举

一个单独的enum会增加app中calsses.dex文件大约1.0到1.4KB的大小。对于复杂系统或共享库,这些添加可以迅速积累。如果可能的话,可以考虑使用@IntDef注释和ProGuard 移除枚举,并将它们转换为整数。这类型转换将保存所有的枚举类型的安全的好处。

减少本地库的大小

如果你的app只用本地代码和android NDK, 你可以优化代码来减少apk的大小. 两种有用的技术:去除调试符号,和 避免提取本地库。

去除调试符号

如果你的程序还在开发中,仍然需要debug,请使用debug符号。使用Android NDK提供的 arm-eabi-strip工具从本地库中移除不需要的debug符号。然后,你编译release版本.

避免提取本地库

保存 apk中未压缩的so文件,和在app的manifest文件中的<application>下面设置android:extractNativeLibs为false.这可以避免 在安装的时候, PackageManager 从apk中拷贝so到文件系统 ,并且这样做的一个额外的好处是,增量更新的时候,增量包更小。

维护多个精品apk


你的apk会包含一些用户从下载apk之后永远不会使用的内容,如地区,语言信息。为你的用户创建一个精简的下载,你能够可以将app分为几个apk;由一些不同的因素如屏幕大小,是否支持GPU 纹理等区别.

当一个用户下载你的app时,他们的设备接收正确的apk,这个apk基于他们的设备特性和设置。这样,设备不需要接收哪些不需要的资源。譬如,如果一个用户有一个hdpi的设备,他们不需要xxxhdpi的资源,这个资源是更高密度的设备显示所需要的。

更多信息,请看 Configure APK Splits and Maintaining Multiple APKs.

缩小apk的大小