您的位置:首页 > 移动开发 > Android开发

Android 之美] APK 瘦身,减少APK的大小

2017-10-19 10:56 253 查看
Android 之美 APK 瘦身减少APK的大小
分析APK的大小

使用Android Studio 22
NimbleDroid
ClassShark

对APK进行瘦身
对资源进行极限压缩
使用WEBPSVG图片资源格式
利用AndResGuard资源压缩打包工具

清除你的代码和资源
清除无用代码

自定义混淆规则

清除无用的资源文件

指定要忽略的资源文件
启用严格的检测

清除未使用的替代资源
合并重复的资源
查看资源回收

使用APK Splits构建APK
按屏幕密度拆分
按 ABI 拆分
使用多版本的APK

资源动态加载
依赖库优化
支持插件化

使用ReDex优化


分析APK的大小

古人云:工欲善其事,必先利其器,我们得先有利器,下面就是我们常用的分析APK大小工具的利器做一些简单的介绍和使用,帮助我们分析造成APK体积较大的元凶,以便于我们从那些方面入手优化。


使用Android Studio 2.2

Android Studio 2.2 新功能直接能分析APK的大小,双击打开就能看到那些占用APK比例大,方法数等。



分析任何的APK
查看APK下载包的大小,解压后的实际大小
反编译资源文件,还原layout中的资源id,代码
分析dex,显示每部分的方法数,直接查看那些library体积比较大

使用方法:Build -> Analyz APK

有了
Analyz APK
这个利器,以下工具也可以基本不用了


NimbleDroid

NimbleDroid 是美国哥伦比亚大学的博士创业团队研发出来的自动化分析Android app性能指标的系统,分析的方式有静态和动态两种方式,其中静态分析可以分析出APK安装包中大文件排行榜,各种知名SDK的大小以及占代码整体的比例,各种类型文件的大小以及占排行,各种知名SDK的方法数以及占所有dex中方法数的比例,针对缓慢的方法,缓慢的第三方SDK和内存泄漏。 

测量生成的速度、网络、内存和磁盘使用率。总之有非常多分析App性能的功能,如果要做性能优化,也可以尝试使用NimbleDroid

查看详细的方法耗时



具体使用方法请看官网: 
https://nimbledroid.com/

不过需要注意的是不要上传任何未发布的产品


ClassShark

ClassShark 是一款查看Android执行文件(apk)的浏览工具,目前有两个android App(Apk)和桌面(jar)的版本。 

使用这款工具,可以很方便的打开APK/Class/Jar/res 

等 文件和分析里面的内容。



具体源码与使用方法详细在github中: 
https://github.com/google/android-classyshark

通过以上任一工具分析我们知道我们项目中主要是以下文件占用APK大小:

classes.dex 

classes.dex是java源码编译后生成的java字节码文件,

res 

主要是存放我们的图片资源

resources.arsc 

编译后的二进制资源文件,非常多无效资源文件(语言)

assets 

主要存放了我们的缓存数据文件,已做最优化压缩,我们考虑能否云端存放。

lib 

主要是存放我们的so库,目前我们已经优化了

既然知道了那些数据导致我们APK体积大,那么我们就着手瘦身了。


对APK进行瘦身


对资源进行极限压缩

对资源进行极限压缩,主要是对如:res里面用到的图片资源文件和assets文件夹下的一些html,db等一些缓存预留在APK的数据文件
assets资源压缩,使用7zip或者lzma压缩方式最高,把文件打包压缩
res 图片资源的压缩,使用tinypng优化Android的资源图片,通常我们可以在保证图片不失真的情况下,多压缩几次。目前tinypng已经支持png和jpg图片、.9图的压缩
将非alpha的图转换成jpg形式

通过以上方法我们图片降低了79%的大小。


使用WEBP,SVG图片资源格式

WebP是谷歌研发出来的一种图片数据格式,它是一种支持有损压缩和无损压缩的图片文件格式,如果应用支持到Android 4.0+,那么我们可以使用WebP格式代替PNG,我们的资源大小能降低50%多。或者有些资源可以使用SVG图片资源更小。 

以下是他们的对比图:



这里提供方便转换的WEBP资源的工具:
https://isparta.github.io/

https://imageoptim.com/mac
使用VectorDrawable和SVG图片来替换原有图片 

使用SVG不用考虑屏幕适配问题,体积非常小。


利用AndResGuard资源压缩打包工具

微信中的资源混淆工具主要为了混淆资源ID长度(例如将res/drawable/icon.png,png变成混淆为r/s/a.png),同时利用7z深度压缩、对png的存储方式做了改变占用内存更小,大大减少了安装包体积

具体源码与使用方法详细在github中:

https://github.com/shwenzhang/AndResGuard


清除你的代码和资源

通过上面的图片资源极限压缩能对APK减小不少,但这往往这些还是不够的,项目里还有很多未使用的资源文件,重复的资源等,这里主要参考Google官方文档https://developer.android.com/studio/build/shrink-code.html#shrink-code 部分,利用Android
Plugin开启gradle 的
Code shrinking
ProGuard
结合使用。

ProGuard
能够检测和删除未使用的类,字段,方法,和从你的打包应用程序的属性,包括那些包含代码库,ProGuard是一个混淆优化字节码的工具,能够删除一些未使用的代码,混淆使用的类,字段,方法和短名称,经过混淆处理也能够使APK源代码得到保护

Code shrinking
是一个Android Plugin for Gradle,从您的打包的应用程序中删除未使用的资源
1547d
,包括代码库中的未使用的资源。它工作在与代码缩小,这样,一旦未使用的代码已被删除,任何资源不再引用可以安全地删除。

该功能需要依赖于:
SDK Tools 25.0.10 或更高
Android Plugin for Gradle 2.0.0 或更高


清除无用代码

code shrinking需要结合ProGuard使用,添加
minifyEnabled
 
true
在你的
build.gradle
文件中。

需要注意code shrinking会减慢Gradle 编译,应避免使用它在您的调试版本中使用它。Android Studio禁用ProGuard使用 Instant Run.。 

例如,以下从
build.gradle
文件片段,使
code
shrinking
为发布版本

android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(‘proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}
1
2
3
4
5
6
7
8
9
10
11

其中
getDefaultProguardFile(‘proguard-android.txt')
默认ProGuard设置来自于Android SDK 
tools/proguard/
中的文件夹

更多的减少代码可以尝试使用相同位置的
proguard-android-optimize.txt
文件(这样能让我们的APK又减少了0.5M以上)proguard-rules.pro是你自定义的proguard规则。

那么我们怎么知道每一次build,删除了和未删除那些资源和代码呢,ProGuard会输出以下文件在
/build/outputs/mapping/release/
:
dump.txt 

描述.apk文件中所有类文件间的内部结构
mapping.txt 

列出了原始的类,方法和字段名与混淆后代码间的映射。这个文件很重要,当你从release版本中收到一个bug报告时,可以用它来翻译被混淆的代码。
seeds.txt 

列出了未被混淆的类和成员
usage.txt 

列出了从.apk中删除的代码
resources.txt 

列出resource被保留的资源

自定义混淆规则


在某些情况下,默认的混淆器配置文件
proguard-android.txt
文件是会移除所有只有未使用的代码,但也有可能会误删除了你需要的代码,所以要注意以下几种情况:
AndroidManifest.xml
配置的文件类
使用了JNI 的接口方法
运行时反射调用方法(不过现在ProGuard已经可以处理这种了)

添加-keep来忽略一下防止被混淆的代码到proguard-rules.pro文件中,比如:

-keep public class MyClass


另外也可以使用@Keep 注解在你的需要忽略的代码中,需要Annotations
Support Library的支持

有关自定义proguard-rules.pro文件的更多信息,可以参考ProGuard
Manual.这里Troubleshooting列出了一些常见的问题。


清除无用的资源文件

Resource shrinking
 需要和
Code shrinking
 一起使用。在代码中删除所有未使用的代码后,
Resource
shrinking
才可以知道哪些资源APK程序仍然使用,你必须先删除未使用的代码,Resource才会成为无用的,从而被清除掉。

添加
shrinkResources
 
true
属性在你的
build.gradle文件中,相应代码块如下:
android {
...
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12

resource shrinker
 目前还不支持移除定义在values/目录下的资源文件(strings,dimensions,styles,colors),因为Android Asset Packaging Tool(AAPT)不允许Gradle
Plugin指定预定义的版本资源[issue 70869]


指定要忽略的资源文件


如果我们希望保留或丢弃特定的资源,需要在项目中创建一个XML文件,并使用
resources
标签,并指定每个资源保存在工具中
tools:keep
和每个资源在工具中丢弃 
tools:discard
。两个属性都可以使用逗号(,)分隔符声明资源名称列表。也可以使用*
作为匹配符,匹配名称。

相应代码块如下:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
tools:discard="@layout/unused2" />
1
2
3
4
5

需要在项目resources目录保存res/raw/keep.xml文件,build的时候该文件不会被打包到APK里面。

启用严格的检测


通常情况下,
resource shrinker
可以准确地确定资源使用。如果你使用
Resources.getIdentifier()
动态获取指定资源的Id,在默认情况下,这样资源具有匹配名称的格式为潜在的使用,无法去除。

例如,下面的代码将导致所有img_前缀的资源都无法去除。
String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());
1
2
3
4

resource shrinker
 也通过搜索代码中是否包含资源名来判断是否在build的时候删除。
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict" />
1
2
3
4

在 resource 文件中指定 shrinkMode,你可以指定 Gradle 在处理该资源文件时候的方式,默认的值为 safe,你也可以将它指定为 strict(只会保留有明确引用的资源,以及处理被 
tools:keep
 和 
tools:discard
 标注的资源)

在后面查看资源回收情况,我们会讲到,会遇到有些xml 无法被清除的问题,使用shrinkMode可以解决这个问题。


清除未使用的替代资源

Gradle 
resource shrinker
 只删除你在代码中未使用资源,这意味着它不会删除不同的设备配置的可替代资源。如果有必要,你可以使用
Android
Gradle plugin
 的
resconfigs
属性删除替代资源文件。

例如:我们项目中适配10种国家语言,而项目依赖了v7、v4等其他support包里面包含20种国家语言,那么我们可以通过resconfigs 删除剩余的可替代资源文件,这对于我们APK大小可减少了不少,

以下代码说明了如何限制你的语言资源,只是英语和法语:
android {
defaultConfig {
...
resConfigs "en", "fr"
}
}
1
2
3
4
5
6
7

像上面那样通过
resconfig
属性指定的语言。未指定的语言的任何资源都被删除。

同样的图片资源我们也可以这么做,例如:我们提供一套xxhdpi的图片资源,其他的都过滤清除掉,这样大量清除了support,其他第三方library的资源文件,关于这个待会我们在后面会说。


合并重复的资源

默认情况下,Gradle也将同名的资源,比如相同的名字,可能是在不同的资源文件夹下,这样子不能通过shrinkResources属性来去除。

只有当两个或多个文件共享相同的资源名称、类型和限定符时才发生资源合并,关于资源的合并优先级如下:

Gradle 主要从以下位置合并资源:
src/main/res/ 主要资源
Gradle 使用variant(build type 和 build flavors)
Library的依赖使用

Gradle合并重复资源优先顺序为:

Dependencies → Main → Build flavor → Build type

例如:如果一个重复的资源在你的mian res中和一个Build flavor指定 ,Gradle 会优先选择Build flavor


查看资源回收

当你Gradle 
resource shrinker
,Gradle Console 输出日志,移除APK资源的信息。例如:
:android:shrinkDebugResources
Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning
1
2
3
4

APK构建完成后会Gradle会生成一个resource.txt 在 /build/outputs/mapping/release/ 中,这个文件包括详细信息,如资源参考其他资源和使用或删除资源的详细信息等。

例如:找出为什么
@drawable/ic_plus_anim_016
,仍然包含在你的APK中,在resource.txt 搜索该文件名,你可能会发现它是被另一个资源引用,如下:
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016
1
2
3

现在需要知道为什么
@drawable/add_schedule_fab_icon_anim
 仍然在使用,搜索我们可以知道应该有代码引用着
add_schedule_fab_icon_anim


如果你不使用严格的检查(就是上面讲的
shrinkMode
),同样的我们如果我们在drawable中使用了字符串’#FFFFFF‘ 这样的使用
resource
shrinker
也不能将他移除在APK中,通常我们可以在Gradle Console中看到以下信息:
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
used because it format-string matches string pool constant ic_plus_anim_%1$d.
1
2
3

那么这种情况我们如何解决呢,可以通过以下方法来清理:
使用tools:discard,在 tools:shrinkMode=”strict” 的时候生效,指定某资源文件需要删除。在你确定该资源文件无效的时候使用。

利用Lint找出未使用的资源并清理掉

在Android Studio中打开“Analyze” 然后选择”Inspect Code…”,范围选择整个项目,然后点击”OK”

到这里APK的大小又小了不少。


使用APK Splits构建APK

虽然我们上面很好的使用了
resource shrinker
可以回收一些未使用的资源(v7、v4、google Service 等Libarry资源),但有些资源仍然未被清除。

例如:那些未使用的多套替代资源,或者是library内部隐患着引用着的资源而我们却没有使用到。或者是我们要根据用户的手机去提供不同版本的APK,如分辨率(xxhdpi,mhdpi等),so库等。那么我们可以使用
APK Splits
大大的减少一些无用的资源,这里我们主要参考了http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 文档。

APK Splits
比起使用 flavors,能让应用程序更有效地构建一些形式的多个apk。

多 apk 只支持以下类型:
屏幕密度
ABI

使用新的
APK Splits
,构建同一个应用程序的hdpi版本和mdpi版本,能够共享很多的任务 (如 javac,dx,proguard)。此外,它会被认为是一个单一的variant,并且同一个测试程序将会被用来测试每​​个多APK。


按屏幕密度拆分

android {
...
splits {
density {
enable true
exclude "ldpi", "tvdpi", "xxxhdpi"
compatibleScreens 'small', 'normal', 'large', 'xlarge'
}
}
1
2
3
4
5
6
7
8
9
10
enable: 启用屏幕密度拆分机制
exclude: 默认情况下所有屏幕密度都包括在内,你可以移除一些密度。
include: 表示要包括哪些屏幕密度
reset(): 重置屏幕密度列表为只包含一个空字符串 (这能够实现,在与include一起使用时可以表示使用哪一个屏幕密度,而不是要忽略哪一些屏幕密度)
compatibleScreens:表示兼容屏幕的列表。这将会注入到manifest中匹配的 节点。这个设置是可选的。

构建完成后可以在out/apk/目录下看到多个版本的APK


按 ABI 拆分

android {
...
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a', 'mips'
universalApk true
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
enable: 启用ABI拆分机制
exclude: 默认情况下所有ABI都包括在内,你可以移除一些ABI。
include:指明要包含哪些ABI
reset():重置ABI列表为只包含一个空字符串(这可以实现,在与include一起使用来可以表示要使用哪一个ABI,而不是要忽略哪一些ABI)
universalApk:指示是否打包一个通用版本(包含所有的ABI)。默认值为 false。

例如:我们项目主要提供xxhdpi的图片资源,而项目中引用到了很多第三方库(v7、v4、google Service 等Libarry资源)我们无法使用到,那么我们可以通过这种方法来去除那些资源。这样我们的APK又减小了非常多。


使用多版本的APK

Multiple APK Support
是一个在Google Play,可以发布不同的应用程序,分别针对不同的设备配置特征。每个APK是一个完整的、独立的应用程序版本,但他们分享在Google Play相同的应用程序清单,必须共享相同的包名和与签名。Google Play 会自动给你匹配相应的APK,这样我们的APK
就可以是分不同版本构建需要资源文件,从而减小APK的大小。

通过发布有多个APK,我们可以:
支持不同OpenGL的APK
支持不同的屏幕尺寸和密度的APK
支持不同的设备功能的APK
支持不同的平台版本的APK

支持不同的CPU架构,每个apk(如ARM、x86,MIPS等)的APK

更多相关信息请参考https://developer.android.com/google/play/publishing/multiple-apks.html#Concepts

目前我们基于这个方案做了不同屏幕的APK。


资源动态加载

我们可以在项目中使用资源动态加载形式,例如:表情,语言,离线库等资源动态加载,减小APK的大小。


依赖库优化

如果应用支持的最低版本是API14,那就不要使用android support库,或者分开使用android support库,用哪个引入哪个库(android-support-ui/android-support-core 等)虽然现在支持还不太好,Google servie 也是如此。
使用更轻量级的库代替,或者优化library的大小,不然自己写更好。
删除armable-v7包下的so、删除x86包下的so,基本上armable的so也是兼容armable-v7的,armable-v7a的库会对图形渲染方面有很大的改进,不过最好的是根据上面我们说的提供多版本APK,对不同的平台精简,再或者动态的加载so。
使用H5编写界面,图片云端获取
资源缓存库不放在assets下,云端获取更新。


支持插件化

未来对于一些独立业务模块,可以做成插件化动态加载,用户需要使用时,只需下载少部分插件。


使用ReDex优化

ReDex是Facebook开源一个减小安卓app大小以提高性能的工具,内嵌以及清除僵尸代码这样的优化来减小字节码,主要是对Dex进行了优化,能让APK 运行更快,不过需要多测试是否会崩溃。

教程很简单具体更详细的内容请参考: 
https://code.facebook.com/posts/998080480282805/open-sourcing-redex-making-android-apps-smaller-and-faster/

github地址: 
https://github.com/facebook/redex.git

转自:http://blog.csdn.net/vfush/article/details/52266843#android-之美-apk-瘦身减少apk的大小
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: