这是 Android 官方文档给出的一些减少包体积的建议,最近正好在看这方面内容,顺手记录、分享一下。本文主要是针对 Android 的资源(Resource) 相关优化建议。
1 移除无用资源
使用 lint 静态代码分析工具查找出未使用的 res
资源。
注意 assert
和 lib
下的未使用资源无法检测出来,其中 assert/ 资源引用采用的是反射方法。 除了检测工具外,Gradle 还支持在打包编译的时候自动移除无用资源,使用 shrinkResources
:
android {
// Other settings
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
这里 minifyEnabled
需要一起打开,即代码压缩。这样编译的时候会先用 R8
进行无用代码移除,然后 gradle 插件进行无用资源的移除。详细使用参考:Shrink, obfuscate, and optimize your app
除此之外,还可以使用 resConfig
配置针对指定的 flavor
来打包指定的资源。比如外发包的图片和第一方的包的资源不一样,配置在不同的文件夹下,打包的时候就可以只打包指定的文件夹资源,这样也能减少一定的体积。
2 减少依赖库大小
开发的时候,在选择第三方工具库时,需要关注一下该库的大小。比如 Android 图片加载库 Glide、Picasso、Fresco的选择,将依赖库的体积大小作为选用的衡量标准之一。
另外,如果只是使用某个工具库的其中一个(小)功能,是否可以考虑将其下载下来自行修改,适当移除不需要的功能代码,从而减少依赖库的体积。
3 原生动画图片支持
使用 ImageDecoder
原生 API 支持带动画的 GIF 和 WebP 的图片文件格式,从而删除第三方库减少包体积。
要求 Android 11 以上才支持
4 支持特定分辨率
从 Android 4.4 开始,支持 ldpi
, mdpi
, tvdpi
, hdpi,
xhdpi
, xxhdpi
和 xxxhdpi
这么多的分辨率(屏幕密度,density),但是没有必要给每个分辨率都制作对应的图片资源。
也许你的用户只有 0.1% 的人需要 ldpi
...
这个时候,其实我们只需要一种分辨率的文件夹,比如 drawable-nodpi/
,对于其他的,系统会自动适配(放大缩小)
官方推荐至少有一种分辨率
xxhdpi
5 使用 drawable 或代码渲染图片
有些单色背景图片其实不需要静态的图片文件,使用 Drawable
对象(xml 里的 )可以在运行的时候绘制出来,从而节省出静态图片文件的大小。
对应的,使用代码来生成某些简单的效果图(render from code),比如页面背景设置为纯色。
相比使用图片文件,可以减小一定的包体积大小。
6 复用资源
某些情况下,两张图片可能除了角度或者颜色不一样,其他都是一样的,这个时候,我们就不推荐使用2张图片,可以通过代码来上色或者旋转。
图片的纯上色(改变颜色),大于 Android 5.0(API 21) 的系统可以使用 android:tint
和 tintMode
,小于 Android 5.0 的版本使用 ColorFilter
来完成。
改变图片的旋转角度,比如上箭头改为下箭头这种,使用下面这样即可:
<?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" />
7 压缩图片资源文件
官方内置的工具 aapt,在编译的时候,会自动对 res/drawable/
(其他目录无效,比如 asset/
)里面的图片进行无损压缩(lossless compression),如果不需要,可以手动关闭:
buildTypes.all { isCrunchPngs = false }
需要注意的是,isCrunchPngs 在 release 默认打开(这会增加编译时间),debug 下默认关闭。
选用第三方工具进行不同格式图片压缩:
- PNG 使用 Pngcrush (推荐)、pngquant、zopfli
8 使用 WebP 文件格式
Android 3.2(API 13) 之后开始支持 WebP 图片格式。
WebP 相比 JPEG 和 PNG 有更好的压缩效率(即压缩的体积更多,损失越小),可以直接在 Android Studio 里面将 BMP, JPG, PNG 或者静态 GIF 图片转换为 WebP 格式。
JPEG 为有损压缩(lossy compression) PNG 为无损数据压缩,主要体现在透明度上的变化。(lossless data compression,a raster-graphics file format)
9 使用矢量图形(vector graphics)
矢量图形是一种与分辨率无关的图片,100字节大小的文件可以描述出充满整个屏幕的高清图片。
矢量图形在代码里用 VectorDrawable
表示。
但是有一定的性能问题。矢量图形本质上就是一系列 xml 数据,Android 系统根据矢量图形的“描述”调用 cpu\gpu 去绘制出来一张图:比如x坐标以上涂满红色,y坐标以下涂满绿色,哪里哪里填上灰色...
适合用于小面积的图标(icon)绘制,不适合大面积的图像绘制,有一定的性能消耗。
例子:
<!-- res/drawable/battery_charging.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<group
android:name="rotationGroup"
android:pivotX="10.0"
android:pivotY="10.0"
android:rotation="15.0" >
<path
android:name="vect"
android:fillColor="#FF000000"
android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V9h4.93L13,7v2h4V5.33C17,4.6 16.4,4 15.67,4z"
android:fillAlpha=".3"/>
<path
android:name="draw"
android:fillColor="#FF000000"
android:pathData="M13,12.5h2L11,20v-5.5H9L11.93,9H7v11.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V9h-4v3.5z"/>
</group>
</vector>
还有动画效果也可以使用 vector graphics 来实现,(对比使用逐帧动画,需要放入大量的单帧图片组合一个动画,极大增加了apk包大小!)