微信公众号

Android APK瘦身实践

为什么瘦身

现在APK动不动就是几十兆,当用户连接到2G和3G网络,或者按流量收费的设备是多么的浪费,这篇文章介绍了如何对APK进行瘦身,帮助更多用户毫不犹顾忌地下载你们的APP。

了解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格式。

优化方案

以我的个人APP【 微言 】实践,未做任何处理,APK大小 11712 KB= 11.43 MB。

开启minifyEnabled混淆代码

在app/build.gradle打开minifyEnabled:

1
2
3
4
5
6
7
android {
buildTypes {
release {
minifyEnabled true
}
}
}

在app/proguard-rules.pro编写混淆规则,根据自己项目依赖的库一一混淆,这里就不提供具体混淆代码了。

经过混淆,APK大小 10906 KB,效果很明显,一下子瘦了0.78 MB。

开启shrinkResources去除无用资源

在app/build.gradle打开shrinkResources:

1
2
3
4
5
6
7
8
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
}
}
}

shrinkResources依赖于minifyEnabled,必须和minifyEnabled一起用,就是打开shrinkResources也必须打开minifyEnabled。

APK大小 10903 KB。

删除未使用到xml和图片

如何知道哪些xml和图片未被使用到?使用Android Studio的Lint,步骤:
Android Studio -> Menu -> Refactor -> Remove Unused Resources
选择 Refactor 一键删除
选择 Perview 预览未使用到的资源

或者

点击菜单栏 Analyze -> Run Inspection by Name -> unused resources -> Moudule ‘app’ -> OK,这样会搜出来哪些未被使用到未使用到xml和图片,如下:

经过删除,APK大小 10891 KB。

删除未使用到代码

同样使用Android Studio的Lint,步骤:点击菜单栏 Analyze -> Run Inspection by Name -> unused declaration -> Moudule ‘app’ -> OK

经过一番清理,APK大小 10880 KB,才瘦了11KB,我以为能瘦很多,不过,平时必须保持良好的编码习惯,哪些没有使用到的代码还是删删掉吧,不要想着以后还会用到。

重复操作上一步和这一步,会有意外的发现哦。

png图片格式转成jpg

将一些大图的格式转成jpg格式,将会有效减小图片的体积,我的APP大图不是很多。

更改格式后,APK大小 10762 KB。

使用vector

微言是我很早就已经做得app,一开始图标都是图片,且每个drawable都有相应尺寸的图标,占了不少大小,后来部分用vector代替了图片。注意一点,Button在xml里设置app:srcCompat居然无效,只能在代码里设置:

1
button.setImageResource(R.drawable.ic_favorite_border_black_24dp)。

APK大小 10754 KB。

使用shape作为背景

很多点击效果可能会使用到图片,可以换成shape是实现,我查看了下,微言APP都是shape实现的,Good。

使用TinyPng

如果对png图片格式转成jpg,减小效果还不满意,直接使用TinyPng智能有损压缩技术,网址:https://tinypng.com/ ,压缩效果:

对于图片资源很多的APP,APK瘦身效果不要太好。

APK大小 10341 KB。

配置resConfigs

如果APP支持中文,可以配置resConfigs,只支持中文

1
2
3
4
5
6
7
android {
defaultConfig {
...
//语言资源,只支持中文
resConfigs "zh"
}
}

APK大小 10243 KB。

使用微信Android资源混淆工具

微信AndResGuard是一个帮助你缩小APK大小的工具,详情:Android资源混淆工具使用说明
使用方法:

Project/build.gradle

1
2
3
4
5
6
7
8
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.0'
}
}

app/build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
apply plugin: 'AndResGuard'
def supportVersion = "25.0.0"
android {
...
signingConfigs {
release {
storeFile file('keystore/android.keystore')
storePassword '123456'
keyAlias 'android.keystore'
keyPassword '123456'
}
debug {
storeFile file('keystore/android.keystore')
storePassword '123456'
keyAlias 'android.keystore'
keyPassword '123456'
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
andResGuard {
// mappingFile = file("./resource_mapping.txt")
//mappingFile用于增量更新,保持本次混淆与上次混淆结果一致;
mappingFile = null
//uss7zip为true时,useSign必须为true;
use7zip = true
//useSign为true时,需要配置signConfig;
useSign = true
//打开这个开关,会keep住所有资源的原始路径,只混淆资源的名字;
keepRoot = false
//whiteList添加在代码内部需要动态获取的资源id,不混淆这部分;
whiteList = [
// for your icon
"R.drawable.icon",
// for fabric
"R.string.com.crashlytics.*",
// for umeng update
"R.string.umeng*",
"R.string.UM*",
"R.string.tb_*",
"R.layout.umeng*",
"R.layout.tb_*",
"R.drawable.umeng*",
"R.drawable.tb_*",
"R.anim.umeng*",
"R.color.umeng*",
"R.color.tb_*",
"R.style.*UM*",
"R.style.umeng*",
"R.id.umeng*",
// umeng share for sina
"R.drawable.sina*",
// for google-services.json
"R.string.google_app_id",
"R.string.gcm_defaultSenderId",
"R.string.default_web_client_id",
"R.string.ga_trackingId",
"R.string.firebase_database_url",
"R.string.google_api_key",
"R.string.google_crash_reporting_api_key",
// umeng share for facebook
"R.layout.*facebook*",
"R.id.*facebook*",
// umeng share for messager
"R.layout.*messager*",
"R.id.*messager*",
// umeng share commond
"R.id.progress_bar_parent",
"R.id.webView"
]
//用来指定文件重打包时是否压缩指定文件;
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"resources.arsc"
]
//sevenzip可使用artifacr或path,path指本地安装的7za(7zip命令行工具)。
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.2.0'
//path = "/usr/local/bin/7za"
}
}

AndResGuard打包命令行:gradlew resguardRelease,最终的混淆APK会生成在{App}/build/output/apk/AndResGuard目录下。

APK大小 10035 KB= 9.79 MB。

使用webp格式

对于 res 文件夹,通常占空间最大的就是图片了。如果你的 Android Studio 为 2.3,并且项目的 minSdkVersion 为 18 或以上,应该使用 webp 而不是 png 图片。webp 图片有更小的体积,图片质量还没有什么损失。

我们可以选中 drawable 和 mipmap 文件夹,右键后选择 convert to webp,将图片转为 webp 格式。

结尾


最终APK瘦身 11.43 - 9.79 = 1.63 MB,瘦身效果不是很明显,说明:
1、我的这个APP毕竟不是商业项目,本身就不大;
2、我的编码习惯可以,没有多少冗余代码和无用资源,哈哈。
大家可以将你们的APK按照这些方法过一遍,看能瘦多少。
瘦身还有其他方法,生命不息,折腾不止。

参考

官方文档Reduce APK Size
https://developer.android.google.cn/topic/performance/reduce-apk-size.html

Android APP终极瘦身指南
http://jayfeng.com/2016/03/01/Android-APP%E7%BB%88%E6%9E%81%E7%98%A6%E8%BA%AB%E6%8C%87%E5%8D%97/

AndResGuard资源混淆
http://www.jianshu.com/p/7ffea26c9fd8