用户通常会避免下载过大的app,尤其是在新兴市场,那里的设备网络连接2G和3G往往参差不齐,或者以按流量收费的形式。这篇文档描述了如何减少你的app的APK大小,它会使得更多的用户下载你的app;
理解APK的结构
--------------------------------------------------------------------------------
在讨论如何减少你的app的大小之前,理解一个app的apk结构是非常有用的。一个APK文件是一个zip压缩文件,它包含你的app的所有文件。这些文件包含Java类文件,资源文件,和一个编译资源的文件。
一个APK包含下面的目录:
META-INF/:包含CERT.SF和CERT.RSA签名文件,和MANIFEST.MF清单文件;
assets/:包含app的assets,app可以使用一个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清单文件。这个文件罗列了名称,版本,访问权限,和app引用的库文件。这个文件使用Android的二进制XML格式;
减少资源数目和大小
--------------------------------------------------------------------------------
你的apk的大小对你的app的加载速度,使用内存大小和消耗功率多少有一定影响。使你的apk变小的一种简单方式就是减少它包含资源的数目和大小。尤其是,你要移除你的app不再使用的资源,并且你可以使用Drawable对象代替图片文件。这个章节讨论了这些,和其它一些能减少你的app资源的方法,来减少你的APK的整体大小。
移除不使用的资源
在Android Studio中的一个静态代码工具lint,检测在你的/res目录下代码没有引用的资源。当lint工具在你的项目中发现一个潜在的未使用的资源,它会打印一条如下示例的消息。
res/layout/preferences.xml: Warning: The resource R.layout.preferences appears
to be unused [UnusedResources]
注意:lint工具没有扫描assets/目录,assets通过反射引用,或者你的app链接的库文件。另外,它不会移除资源;它仅仅是提醒你它们的存在;
你添加到你的代码中的库可能包含未使用的资源。如果你在你的app的build.gradle文件中启动shrinkResourcesGradle,它能自动替你删除这些资源。
android{
// Other settings
buildTypes{
release{
minifyEnabledtrue
shrinkResourcestrue
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
}
为了使用shrinkResources,你必须启动代码压缩。在构建的过程中,首先ProGuard移除了没有使用的代码,但是还存在没有使用的资源。然后Gradle会移除没有使用的资源。
更多关于ProGuard的信息,和其它Android Studios帮助你减少APK大小的方式,查阅Shrink Your Code and Resources。
在Android Gradle插件0.7或者更高版本,你可以声明你的app支持该配置。Gradle通过使用resConfig和resConfigs flavor和defaultConfig向构建系统传递信息。构建系统然后禁止在APK中出现其它来源的资源,不支持的配置,减小APK的大小。更多的关于这个功能的信息,请查阅Remove unused alternative resources。
减少库中使用的资源
在开发Android app的时候,你通常会使用扩展库来提升你的app的可用性和多功能性。例如,你可能引用Android Support Library来提升在老设备上的用户体验,或者你可能使用Google Play Services来检索自动翻译你的app中的文本。
如果一个库针对server和desktop设计,它会包含许多你的app不需要的对象和方法。为了仅仅包含你的app需要的库的这部分,如果允许你修改这个库,你可以编辑这个库的文件。你也可以使用一个可选的,针对移动端设计的库来为你的app添加具体的功能。
注意:ProGuard能清除导入的库不必要的代码,但是它不能移除库庞大的内部依赖。
只支持特定的密度
Android支持一个非常大的设备集合,包括各种屏幕密度。在Android4.4(API Level 19)和更高版本,framework支持各种各样的密度:ldpi,mdpi,tvdpi,xhdpi,xxhdpi和xxxdpi。尽管Android支持所有这些密度,你不需要为每个密度制作资源。
如果你知道你的用户只有一小部分使用特定密度,考虑下你是否需要将这些密度捆绑你的app中。如果你不包含某一指定屏幕密度的资源,Android自动缩放存在的,原来为其它屏幕密度设计的资源。
如果你的app仅仅需要被缩放的图片,你可以通过在drawable-nodpi/目录中保存图片的一个变体,来节省更多的空间。我们要求任何app至少包含一个xxxhdpi图片变体;
更多关于屏幕密度的信息,请查阅Screen Sizes and Densities。
减少动画帧
帧动画可以彻底减少你的APK的大小。下图展示了一个帧动画被分离成多张图片保存在目录中的例子。每个图片在动画中是为一帧。
对于你添加到动画中的每一帧,你增加了在APK中图片的数量。如下图,在app中这个图片动画30帧。如果图片动画用仅仅15帧代替,这个动画仅仅需要一半数量的帧。
使用Drawable对象
一些图片不需要一个静态的图片资源;framework能运行时动态的绘制图片来替代。Drawable对象(在XML中的<shape>)占用你的APK较少的空间。另外,XML Drawable对象能产生符合材料设计指南的纯色图片。
重用资源
你可以为一个图片的变种使用一个单独资源,例如同一个图片的着色,阴影,或者旋转版本。我们推荐使用,然而你视运行时需要,重用同一个资源集合和定制它们。
Android提供了多种方式来改变asset的颜色,在Android5.0(API level 21)或者更高版本,使用android:tint和tintMode的任意一个。对于更低版本的平台,使用ColorFilter类。
你也可以省略资源,当它等同于仅仅将其它资源旋转的时候。下面的代码片段提供了一个示例,通过对原来图片简单的180度旋转,将一个“展开”箭头转换为“收起”箭头。
<?xml version="1.0"encoding="utf-8"?>
<rotatexmlns: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的大小。代码渲染减少了空间,因为不在你的APK中保存图片文件。
处理PNG文件
appt工具可以在构建过程中,使用无损压缩的方式优化放在/res/drawable目录下的图片资源。例如,aapt工具能将一个不需要256颜色真色彩的PNG转化为8-bit调色板的PNG。这样做的结果是一个相同质量的图片但是消耗更小的内存。
记住aapt有如下限制:
aapt工具不会压缩在asset/目录下的PNG文件;
aapt工具只优化使用256或者更少的颜色的图片文件;
aapt工具可能会增加已经被压缩过的PNG图片。为了防止这个,你可以在Gradle中使用cruncherEnabled标识来取消PNG图片的优化过程;
aaptOptions{
cruncherEnabled=false
}
压缩PNG和JPEG文件
你可以使用如pngcrush,pngquant或者aopflipng来减小PNG文件的大小,并且不降低图片的质量。所有的这些工具都可以减少图片的大小并且保持图片的质量。
pngcrush工具尤其有效:这个工具迭代PNG过滤器和zlib参数,使用每个过滤器组合和参数来压缩图片。然后选择最小压缩配置输出。
对于JPEG文件,你可以使用如packJPG工具来将JPEG文件压缩为更紧凑格式。
使用webp文件格式
你可以使用WebP文件格式,代替使用PNG或者JPEG文件。Webp格式支持有损压缩(像JPEG)和透明度(如PNG),但比JPEG或PNG的压缩更好。
使用WebP文件格式有几个显著的缺点。首先,在低于Android3.2系统版本(API level 13)不支持Webp格式。其次,系统解码WebP相比PNG图片会消耗了更多的时间。
注意:Google Play仅仅接受使用PNG格式图标的APK。如果你想通过Google Play发布你的app的话,你不能使用其它文件格式如JPEG或者WebP用户app的图标。
使用矢量图
你可以使用矢量图来创建独立于分辨率的图标和其它可扩展的媒体。使用这些图标可以大大减少你的apk。矢量图在Android中以VectorDrawable对象代表。使用VectorDrawable对象,一个100-byte文件可以生成一个屏幕大小清晰图像。
然而,每个VectorDrawable对象的渲染明显的消耗了系统时间,并且更大的图片需要更长的时间来展示在屏幕上。因此仅仅在显示小的图片的时候考虑使用矢量图。
更新关于使用VectorDrawable对象的信息,请查阅Working With Drawables。
减少Native和Java代码
--------------------------------------------------------------------------------
这里有几种你可以用来减少app的Java和native代码大小的方式。
删除不必要生成的代码
一定要明白任何自动生成代码的足迹。例如,许多协议缓冲工具生成过多的方法和类,它会两三倍的增加你的app的大小。
移除枚举
一个枚举会增加你的app的class.dex文件大约1.0到1.4K的大小。对于复杂的系统或者共享库这种情况会更加明显。如果可能的话,考虑使用@IntDef和ProGuard来剥离枚举,并转换成Integer。这种类型转换保留了所有枚举的安全效益。
减少Native库的大小
如果你的app使用了native代码和Android NDK,你也可以通过优化你的代码减少你的app的大小。两个有用的技术是移除Debug符号和避免取native库。
移除Debug符号
使用debug符号意味着着你的应用程序正在开发并且持续需要debug。使用在Android NDK中提供的arm-dabi-strip工具,来移除不必要的native库的debug符号。在这之后,你可以编译你的release构建。
避免提取Native库
保存APK中解压的.so文件,并且设置在你的应用清单文件中<application>元素中的android:extractNativeLibs标识为false。这将会阻止PackageManager在安装的过程中从APK中复制出.so文件到文件系统中,并且将增加使得你的app的增量更新更小的好处。
维护多个精简的APK
--------------------------------------------------------------------------------
你的apk包含了用户下载但是从来不使用的内容,例如区域的或者语言信息。为了给你的用户提供一个最小的下载,你可以以屏幕尺寸或者GPU类型划分,将你的app分给为多个APK。
当一个用户下载你的应用的时候,他们的设备根据设备的功能和配置获取对应的APK。这种方式下,设备不会获取它没有功能的资源。例如,如果一个用户有一个hdpi的设备,他们不需要xxxhdpi资源,你可能为了更高的密度设备显示而包含。
更多信息,查阅Configure APK Splits和Maintaining MUltiple APKs。
文档url:https://developer.android.com/topic/performance/reduce-apk-size.html