一.概述
众所周知,App包体积大小的重要性不言而喻,对于客户来说,小体积的包意味着更少的消耗流量,更快的下载时间,也意味着更多客户愿意下载尝试,意味着客户量的增加。
随着日常业务的迭代,饿了么的物流App在最新的版本中渐渐达到22M,虽然在日常开发中我们就有着严格要求尽可能的不引入大型三方库,使用尽可能小的图片等来控制App的大小,但是随着突破20M大关,团队决定来一次整体优化。
二.预览
首先对App进行分析中,我们可以看到APK的构成。
APK包含以下目录:
META-INF/:包含CERT.SF和CERT.RSA签名文件,以及MANIFEST.MF清单文件。
assets/:assets包下存放的资源,这个文件夹下面的资源文件是不会被编译优化的。
-
resources.arsc:App已编译的资源的资源索引表。此文件包含来自res / values /文件夹的所有配置的XML内容。
-
res/:存放 drawable,layout,anim,color,menu,raw等资源。
lib/:包含特定处理器的软件层的编译代码。此目录包含每个平台类型的子目录,如armeabi,armeabi-v7a,arm64-v8a,x86,x86_64和mips。
classes.dex:dex字节码文件。
AndroidManifest.xml:包含核心Android清单文件。
Apk中的res下面的资源已经占到了6.7M,classes.dex文件加起来有7.6M。因此,我们打算先从这两个地方开始下手。
三.优化方案
A.classes.dex代码压缩
1.混淆代码
在gradle使用minifyEnabled进行Proguard混淆的配置,可大大减小APP大小:
android {
buildTypes {
release {
minifyEnabled true
}
}
}
2.Proguard
Proguard是编译时对java代码进行压缩,混淆,优化,预编译等操作的集成化工具。Proguard
会去掉无用的代码。Proguard也会混淆重命名代码。
具体的proguard介绍有很多文章,我们就不再这里赘述。
因为之前的代码缘故,我们在proguard-rules.pro中开启了这两个命令关掉了proguard的压缩无用代码和优化dex字节码的功能
-dontshrink
-dontoptimize
因此我们现在去掉这两个命令,重新打包查看.
我们看到缩小300KB,具体的变化在于class.dex大小
3.开启shrinkResources去除无用资源
在app/build.gradle打开shrinkResources:
android {
buildTypes {
release {
...
minifyEnabled true
shrinkResources true
...
}
}
}
shrinkResources必须和minifyEnabled一起用,shrinkResources要生效也必须打开minifyEnabled。
开启后效果非常明显,缩小了600K。
4.as自带的lint检查工具
删除无用代码:使用Android Studio的Lint,步骤:点击菜单栏 Analyze -> Run Inspection by Name -> unused declaration
删除无用资源:使用Android Studio的Lint,步骤:点击菜单栏 Analyze -> Run Inspection by Name -> unused Resource
然后就会检查出很多无用的东西,自行去进行代码的删除检查等等工作
B.配置resConfigs删去多余的语言包
因为项目目前只要支持中文,所以我们决定只保留中文包
android {
defaultConfig {
...
//只支持中文
resConfigs "zh"
}
}
可以看到效果是非常明显的,小了快700KB
C.res下资源处理
接下来我们看到在res文件下,drawable文件是占据了大头,因此我们要对图片进行一些处理。
1.使用一套图片
因为android虽然要进行各种屏幕分辨率的适配,但是其实我们只需要xhdpi或xxhdpi版本的一套图片即可,因为图片也会在不同的分辨率手机上进行缩放自适应。
2.png图片格式转成jpg
由于UED切的图都是png格式,而在项目中一些没有透明度的图片,我们都会将其变为jpg格式,按照往常经验来说,一张png图片转化为jpg后可以减少一半以上的大小。
3.使用其他压缩工具对图片进行多次压缩
在平时开发过程中,尤其要随时注意图片的大小问题,在Apk检查的时候,要查看图片大小是否异常,如果一张图上了20或者30kb,那么就要注意一下是否可以进行压缩。
android在打包过程中aapt会对图片资源大小进行优化,但是个人觉得效果还没有到想要的效果,因此我们选择了tinypng工具进行图片的压缩。
https://tinypng.com/
在压缩时可以多次压缩,尽量在保证图片不失色彩的情况下压缩到尽可能的小。
例如下图,我们在项目中发现这张图没有透明度,并且达到了98k,于是先将其通过在线转化工具转为jpg格式,然后通过tintpng多次压缩,最终选取了一张效果不错,大小适合的。
可以看到,前后缩小了整整61KB,在项目中还存在着很多这样的图片可以进行优化,同时我们也建议在平时开发过程中开发的小伙伴们就要随时注意自己使用的图片大小问题。
其中有一点需要注意的是,我们发现.9图虽然使用了tinypng进行压缩,但是tinpng是将颜色位数压缩,例如把一张24-bit的图压为8-bit后,大小缩小近3倍,但是经过aapt优化后,会把.9图变回24-bit位数。
3.webp图片格式
WebP是谷歌提供的一种支持有损压缩和无损压缩的图片文件格式,而且可以提供比JPEG或PNG更好的压缩。
在Android 4.0(API level 14)中支持有损的WebP图像,在Android 4.3(API level 18)和更高版本中支持无损和透明的WebP图像。
可以看到webp可以说是极大的减小了图片的大小尺寸
4.去掉重复资源
项目中因为使用模块化的原因,所以在某些时间紧迫的迭代中,在不同的模块中引入了重复的图片,color值等等,可以通过一些工具查重等找出重复资源,放在基础模块中供给所有上层模块使用
最后经过上面的处理后,Apk减少到了19.5M。
5.语音文件
在项目中用到了语音文件,我们推荐使用ogg的格式,Ogg文件格式的音质和大小相对于其他语音格式来说已经算很好的。
6.使用代码实现图片背景效果
一些圆角背景的效果,其实可以用shape的xml文件进行实现,尽量减少简单效果图片的使用。
三.lib
对于lib下的各种so文件包,我们在项目中只保留了armeabi包下的so文件。
针对Android 系统的这些拷贝策略的问题,我们给出了一些配置so的建议:
5.1 针对armeabi和armeabi-v7a两种abi
由于armeabi-v7a指令集兼容armeabi指令集,如果可以接受损失一些性能,可以移除armeabi-v7a目录和其下的库文件,只保留armeabi目录。
目前市面上的x86机型,为了兼容arm指令,基本都内置libhoudini模块,即二进制转码支持,该模块负责把ARM指令转换为x86指令,可以选择删掉x86库目录,x86下配置的armeabi目录的so库一样可以正常加载使用。
如果App打算支持64位,那么64位的so要放全,否则可以选择不单独编译64位的so,全部使用32位的so,64位机型默认支持32位so的加载。比如Apk使用第三方的so只有32位ABI的so,可以考虑去掉Apk中lib目录下的64位ABI子目录,保证Apk安装后正常使用。
四.微信Android资源混淆工具
https://github.com/shwenzhang/AndResGuard
AndResGuard是一个帮助你缩小APK大小的工具,他的原理类似Java Proguard,但是只针对资源。他会将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a。
AndResGuard不涉及编译过程,只需输入一个apk(无论签名与否,debug版,release版均可,在处理过程中会直接将原签名删除),可得到一个实现资源混淆后的apk(若在配置文件中输入签名信息,可自动重签名并对齐,得到可直接发布的apk)以及对应资源ID的mapping文件。
这是一个微信团队和腾讯很多团队都在使用的工具,具体介绍都在上面的链接中。效果还是非常明显,在我们的项目中缩小了0.8M的大小。
五.业务改造
对于一些需要用到大量图片资源的场景。例如一些新手教程,图例等,如果不会经常性的变动,可以尝试在某些场景使用代码实现效果来替代图片效果,还比如一些控件背景圆角等效果也可以用shape.xml来进行替换。因为业务需求,我们的新手教程全部为图片,我们进行改造后减少了700kB大小。