小丸工具箱官方网站
作者:fuchenxuan
出处:
欢迎访问小丸工具箱压缩更小我的个人站点:
转载请注明出处–
让你的apk文件尽可能小小丸工具箱压缩更小,应该使移除未使用的代码和资源文件。那么本章节介绍了如何做到让APK更小小丸工具箱压缩更小,性能更好小丸工具箱压缩更小,下载转化率会更高,以及如何指定在构建APK过程中保留或移除的代码和资源,在我们还没有分析APK大小之前,项目中存在一些资源放置处理不当,没有统一的规范,依赖管理不合理,资源重叠,dex方法数过多等问题,导致APK文件比较大,公司要求APK体积大小要优化到3M左右。经过我们的努力终于达到要求,然而我们发现还能再小。
分析APK的大小
正所谓工欲善其事,必先利其器,我们得现有利器,下面就是我们常用的分析APK大小工具的利器。
使用Android Studio 2.2
Android Studio 2.2 新功能直接能分析APK的大小,双击打开就能看到那些占用APK比例大,方法数等。
分析任何的APK
查看APK下载包的大小,解压后的实际大小
反编译资源文件,还原layout中的资源id,代码
分析dex,显示每部分的方法数,直接查看那些library体积比较大
分析任何的APK
展开全文
查看APK下载包的大小,解压后的实际大小
反编译资源文件,还原layout中的资源id,代码
分析dex,显示每部分的方法数,直接查看那些library体积比较大
使用方法:Build -> Analyz APK
有了 Analyz APK这个利器,以下工具也可以基本不用了
NimbleDroid
是美国哥伦比亚大学的博士创业团队研发出来的自动化分析Android app性能指标的系统,分析的方式有静态和动态两种方式,其中静态分析可以分析出APK安装包中大文件排行榜,各种知名SDK的大小以及占代码整体的比例,各种类型文件的大小以及占排行,各种知名SDK的方法数以及占所有dex中方法数的比例,针对缓慢的方法,缓慢的第三方SDK和内存泄漏。
测量生成的速度、网络、内存和磁盘使用率。总之有非常多分析App性能的功能,如果要做性能优化,也可以尝试使用 。
查看详细的方法耗时
具体使用方法请看官网:
不过需要注意的是 不要上传任何未发布的产品。
ClassShark
ClassShark 是一款查看Android执行文件(apk)的浏览工具,目前有两个android App(Apk)和桌面(jar)的版本。
使用这款工具,可以很方便的打开APK/Class/Jar/res
等 文件和分析里面的内容。
具体源码与使用方法详细在github中:
通过以上任一工具分析我们知道我们项目中主要是以下文件占用APK大小:
classes.dexclasses.dex是java源码编译后生成的java字节码文件,
res主要是存放我们的图片资源
resources.arsc编译后的二进制资源文件,非常多无效资源文件(语言)
assets主要存放了我们的缓存数据文件,已做最优化压缩,我们考虑能否云端存放。
lib主要是存放我们的so库,目前我们已经优化了
classes.dexclasses.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形式
assets资源压缩,使用7zip或者lzma压缩方式最高
res 图片资源的压缩,使用tinypng优化Android的资源图片,通常我们可以在保证图片不失真的情况下,多压缩几次。目前tinypng已经支持png和jpg图片、.9图的压缩
将非alpha的图转换成jpg形式
通过以上方法我们图片降低了79%的大小。
使用WEBP,SVG图片资源格式
WebP是谷歌研发出来的一种图片数据格式,它是一种支持有损压缩和无损压缩的图片文件格式,如果应用支持到Android 4.0+,那么我们可以使用WebP格式代替PNG,我们的资源大小能降低50%多。或者有些资源可以使用SVG图片资源更小。
以下是他们的对比图:
WebP是谷歌研发出来的一种图片数据格式,它是一种支持有损压缩和无损压缩的图片文件格式,如果应用支持到Android 4.0+,那么我们可以使用WebP格式代替PNG,我们的资源大小能降低50%多。或者有些资源可以使用SVG图片资源更小。
以下是他们的对比图:
这里提供方便转换的WEBP资源的工具:
使用VectorDrawable和SVG图片来替换原有图片
使用SVG不用考虑屏幕适配问题,体积非常小。
使用VectorDrawable和SVG图片来替换原有图片
使用SVG不用考虑屏幕适配问题,体积非常小。
使用VectorDrawable和SVG图片来替换原有图片
使用SVG不用考虑屏幕适配问题,体积非常小。
微信中的资源混淆工具主要为了混淆资源ID长度(例如将res/drawable/icon.png,png变成混淆为r/s/a.png),同时利用7z深度压缩、对png的存储方式做了改变占用内存更小,大大减少了安装包体积
具体源码与使用方法详细在github中:
清除你的代码和资源
通过上面的图片资源压缩能对APK减小不少,但这往往还不够,项目里还有很多未使用的资源文件,重复的资源等,这里主要参考Google官方文档 部分,利用Android Plugin开启gradle 的 Code shrinking和 ProGuard结合使用。
ProGuard能够检测和删除未使用的类,字段,方法,和从你的打包应用程序的属性,包括那些包含代码库, 是一个混淆优化字节码的工具,能够删除一些未使用的代码,混淆使用的类,字段,方法和短名称,经过混淆处理也能够使APK源代码得到保护
Code shrinking是一个Android Plugin for Gradle,从您的打包的应用程序中删除未使用的资源,包括代码库中的未使用的资源。它工作在与代码缩小,这样,一旦未使用的代码已被删除,任何资源不再引用可以安全地删除。
ProGuard能够检测和删除未使用的类,字段,方法,和从你的打包应用程序的属性,包括那些包含代码库, 是一个混淆优化字节码的工具,能够删除一些未使用的代码,混淆使用的类,字段,方法和短名称,经过混淆处理也能够使APK源代码得到保护
Code shrinking是一个Android Plugin for Gradle,从您的打包的应用程序中删除未使用的资源,包括代码库中的未使用的资源。它工作在与代码缩小,这样,一旦未使用的代码已被删除,任何资源不再引用可以安全地删除。
该功能需要依赖于:
SDK Tools 25.0.10 或更高
Android Plugin for Gradle 2.0.0 或更高
SDK Tools 25.0.10 或更高
Android Plugin for Gradle 2.0.0 或更高
code shrinking需要结合ProGuard使用,添加 minifyEnabledtrue在你的 build.gradle文件中。
需要注意code shrinking会减慢Gradle 编译,应避免使用它在您的调试版本中使用它。Android Studio禁用ProGuard使用 .。
例如,以下从 build.gradle文件片段,使 code shrinking为发布版本
android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(‘proguard-android.txt'), 'proguard-rules.pro' } } ...}
其中 getDefaultProguardFile(‘proguard-android.txt')默认ProGuard设置来自于Android SDK tools/proguard/中的文件夹
更多的代码减少可以尝试使用相同位置的 proguard-android-optimize.txt文件(这里我们又减少了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被保留的资源
dump.txt描述.apk文件中所有类文件间的内部结构
mapping.txt列出了原始的类,方法和字段名与混淆后代码间的映射。这个文件很重要,当你从release版本中收到一个bug报告时,可以用它来翻译被混淆的代码。
seeds.txt列出了未被混淆的类和成员
usage.txt列出了从.apk中删除的代码
resources.txt列出resource被保留的资源
在某些情况下,默认的混淆器配置文件 proguard-android.txt文件是会移除所有只有未使用的代码,但也有可能会误删除了你需要的代码,所以要注意以下几种情况:
在 AndroidManifest.xml配置的文件类
使用了JNI 的接口方法
运行时反射调用方法(不过现在ProGuard已经可以处理这种了)
在 AndroidManifest.xml配置的文件类
使用了JNI 的接口方法
运行时反射调用方法(不过现在ProGuard已经可以处理这种了)
添加-keep来忽略一下防止被混淆的代码到proguard-rules.pro文件中,比如:
-keep public class MyClass
另外也可以使用 注解在你的需要忽略的代码中,需要 的支持
有关自定义proguard-rules.pro文件的更多信息,可以参考 这里列出了一些常见的问题。
清除无用的资源文件
Resource shrinking 只与Code shrinking 一起工作。在代码中删除所有未使用的代码后,Resource shrinking才可以识别哪些资源的应用程序仍然使用,你必须先删除未使用的代码,Resource才会成为无用的,从而被清除掉。
添加 shrinkResourcestrue属性在你的 build.gradle文件中,相应代码块如下:
android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'} }}
resource shrinker 目前还不支持移除定义在values/目录下的资源文件(strings,dimensions,styles,colors),因为Android Asset Packaging Tool(AAPT)不允许Gradle Plugin指定预定义的版本资源
指定要忽略的资源文件
如果您希望保留或丢弃特定的资源,请在项目中创建一个XML文件,并使用“资源”标签,并指定每个资源保存在工具中:保持属性和每个资源在工具中丢弃:丢弃属性。两个属性都接受一个逗号分隔的资源名称列表。你可以使用星号作为外卡
相应代码块如下:
<?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"/>
需要在项目resources目录保存res/raw/keep.xml文件,build的时候该文件不会被打包到APK里面。
启用严格的检测
通常情况下,资源产品可以准确地确定资源使用。如果你使用 Resources.getIdentifier()动态获取指定资源的Id,在默认情况下,这样资源具有匹配名称的格式为潜在的使用,无法去除。
例如,下面的代码将导致所有img_前缀的资源都无法去除。
String name = String.format("img_%1d", angle + 1);res = getResources().getIdentifier(name, "drawable", getPackageName());
resource shrinker也通过搜索代码中是否包含资源名来判断是否在build的时候删除。
<?xml version="1.0" encoding="utf-8"?><resources xmlns:tools="http://schemas.android.com/tools"tools:shrinkMode="strict"/>
在 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" }}
像上面那样通过resconfig属性指定的语言。未指定的语言的任何资源都被删除。
同样的图片资源我们也可以这么做,例如:我们提供一套xxhdpi的图片资源,其他的都过滤清除掉,这样大量清除了support,其他第三方library的资源文件,关于这个待会我们在后面会说。
合并重复的资源
默认情况下,Gradle也将同名的资源,比如相同的名字,可能是在不同的资源文件夹下,这样子不能通过shrinkResources属性来去除。
只有当两个或多个文件共享相同的资源名称、类型和限定符时才发生资源合并,关于资源的合并优先级如下:
Gradle 主要从以下位置合并资源:
src/main/res/ 主要资源
Gradle 使用variant(build type 和 build flavors)
Library的依赖使用
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:shrinkDebugResourcesRemoved unused resources:Binary resource data reduced from 2570KB to 1711KB: Removed 33%:android:validateDebugSigning
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=true16:25:48.009[QUIET] [system.out] @drawable/ic_plus_anim_016
现在需要知道为什么 @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] Markingdrawable:ic_plus_anim_016:2130837506 usedbecauseitformat-stringmatchesstringpoolconstantic_plus_anim_%1$d.
那么这种情况我们如何解决呢,可以通过以下方法来清理:
使用tools:discard,在 tools:shrinkMode=”strict” 的时候生效,指定某资源文件需要删除。在你确定该资源文件无效的时候使用。
利用Lint找出未使用的资源并清理掉
在Android Studio中打开“Analyze” 然后选择”Inspect Code…”,范围选择整个项目,然后点击”OK”
使用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大大的减少一些无用的资源,这里我们主要参考了 文档。
APK Splits比起使用 flavors,能让应用程序更有效地构建一些形式的多个apk。
多 apk 只支持以下类型:
屏幕密度
ABI
屏幕密度
ABI
使用新的 APK Splits,构建同一个应用程序的hdpi版本和mdpi版本,能够共享很多的任务 (如 javac,dx,proguard)。此外,它会被认为是一个单一的variant,并且同一个测试程序将会被用来测试每个多APK。
按屏幕密度拆分
android { ... splits { density { enable true exclude "ldpi", "tvdpi", "xxxhdpi"compatibleScreens 'small', 'normal', 'large', 'xlarge'} }
enable: 启用屏幕密度拆分机制
exclude: 默认情况下所有屏幕密度都包括在内,你可以移除一些密度。
include: 表示要包括哪些屏幕密度
reset(): 重置屏幕密度列表为只包含一个空字符串 (这能够实现,在与include一起使用时可以表示使用哪一个屏幕密度,而不是要忽略哪一些屏幕密度)
compatibleScreens:表示兼容屏幕的列表。这将会注入到manifest中匹配的 节点。这个设置是可选的。
enable: 启用屏幕密度拆分机制
exclude: 默认情况下所有屏幕密度都包括在内,你可以移除一些密度。
include: 表示要包括哪些屏幕密度
reset(): 重置屏幕密度列表为只包含一个空字符串 (这能够实现,在与include一起使用时可以表示使用哪一个屏幕密度,而不是要忽略哪一些屏幕密度)
compatibleScreens:表示兼容屏幕的列表。这将会注入到manifest中匹配的 节点。这个设置是可选的。
构建完成后可以在out/apk/目录下看到多个版本的APK
按 ABI 拆分
android { ... splits { abi { enable truereset() include'x86', 'armeabi-v7a', 'mips'universalApk true} }}
enable: 启用ABI拆分机制
exclude: 默认情况下所有ABI都包括在内,你可以移除一些ABI。
include:指明要包含哪些ABI
reset():重置ABI列表为只包含一个空字符串(这可以实现,在与include一起使用来可以表示要使用哪一个ABI,而不是要忽略哪一些ABI)
universalApk:指示是否打包一个通用版本(包含所有的ABI)。默认值为 false。
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
更多相关信息请参考
支持不同OpenGL的APK
支持不同的屏幕尺寸和密度的APK
支持不同的设备功能的APK
支持不同的平台版本的APK
支持不同的CPU架构,每个apk(如ARM、x86,MIPS等)的APK
更多相关信息请参考
目前我们基于这个方案做了不同屏幕的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下,云端获取更新。
如果应用支持的最低版本是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 运行更快,不过需要多测试是否会崩溃。
教程很简单具体更详细的内容请参考:
github地址:
关于APK瘦身就我们就总结到这了,应该还有很多更好的方案,若有错漏,欢迎补充。
APP架构师
APP架构师是一个数十万开发者探讨APP开发架构的公众号,分享最有价值的干货文章,我们探讨Android性能优化,Android内存泄露,动态化、插件化等最新的Android开发技术,还有IOS开发架构,高级开发知识,我们的愿景是服务每个APP开发者,做一个有逼格的APP架构师小丸工具箱压缩更小!
APP架构师是一个数十万开发者探讨APP开发架构的公众号,分享最有价值的干货文章,我们探讨Android性能优化,Android内存泄露,动态化、插件化等最新的Android开发技术,还有IOS开发架构,高级开发知识,我们的愿景是服务每个APP开发者,做一个有逼格的APP架构师!
看完文章,顺便点击下方广告 ,打赏一下小编,非常感谢!