让你的apk文件尽量小,应该使移除未使用的代码和资源文件。那么本章节介绍了如何作到让APK更小,性能更好,下载转化率会更高,以及如何指定在构建APK过程当中保留或移除的代码和资源,在咱们尚未分析APK大小以前,项目中存在一些资源放置处理不当,没有统一的规范,依赖管理不合理,资源重叠,dex方法数过多等问题,致使APK文件比较大,公司要求APK体积大小要优化到3M左右。通过咱们的努力终于达到要求,然而咱们发现还能再小。javascript
正所谓工欲善其事,必先利其器,咱们得现有利器,下面就是咱们经常使用的分析APK大小工具的利器。css
Android Studio 2.2 新功能直接能分析APK的大小,双击打开就能看到那些占用APK比例大,方法数等。html
使用方法:Build -> Analyz APKjava
有了Analyz APK
这个利器,如下工具也能够基本不用了linux
NimbleDroid 是美国哥伦比亚大学的博士创业团队研发出来的自动化分析Android app性能指标的系统,分析的方式有静态和动态两种方式,其中静态分析能够分析出APK安装包中大文件排行榜,各类知名SDK的大小以及占代码总体的比例,各类类型文件的大小以及占排行,各类知名SDK的方法数以及占全部dex中方法数的比例,针对缓慢的方法,缓慢的第三方SDK和内存泄漏。
测量生成的速度、网络、内存和磁盘使用率。总之有很是多分析App性能的功能,若是要作性能优化,也能够尝试使用NimbleDroid。android
查看详细的方法耗时git
具体使用方法请看官网:
https://nimbledroid.com/github
不过须要注意的是不要上传任何未发布的产品。web
ClassShark 是一款查看Android执行文件(apk)的浏览工具,目前有两个android App(Apk)和桌面(jar)的版本。
使用这款工具,能够很方便的打开APK/Class/Jar/res
等 文件和分析里面的内容。android-studio
具体源码与使用方法详细在github中:
https://github.com/google/android-classyshark
经过以上任一工具分析咱们知道咱们项目中主要是如下文件占用APK大小:
classes.dex
classes.dex是java源码编译后生成的java字节码文件,
res
主要是存放咱们的图片资源
resources.arsc
编译后的二进制资源文件,很是多无效资源文件(语言)
assets
主要存放了咱们的缓存数据文件,已作最优化压缩,咱们考虑可否云端存放。
lib
主要是存放咱们的so库,目前咱们已经优化了
既然知道了那些数据致使咱们APK体积大,那么咱们就着手瘦身了。
对资源进行极限压缩,主要是如res里面用到的图片资源文件和assets的html,db等一些缓存预留在APK的数据文件
经过以上方法咱们图片下降了79%的大小。
这里提供方便转换的WEBP资源的工具:
微信中的资源混淆工具主要为了混淆资源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,从您的打包的应用程序中删除未使用的资源,包括代码库中的未使用的资源。它工做在与代码缩小,这样,一旦未使用的代码已被删除,任何资源再也不引用能够安全地删除。
该功能须要依赖于:
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' } } ... }
其中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/
:
在某些状况下,默认的混淆器配置文件proguard-android.txt
文件是会移除全部只有未使用的代码,但也有可能会误删除了你须要的代码,因此要注意如下几种状况:
AndroidManifest.xml
配置的文件类添加-keep来忽略一下防止被混淆的代码到proguard-rules.pro文件中,好比:
-keep public class MyClass
另外也可使用@Keep 注解在你的须要忽略的代码中,须要Annotations Support Library的支持
有关自定义proguard-rules.pro文件的更多信息,能够参考ProGuard Manual.这里Troubleshooting列出了一些常见的问题。
Resource shrinking 只与Code shrinking 一块儿工做。在代码中删除全部未使用的代码后,Resource shrinking才能够识别哪些资源的应用程序仍然使用,你必须先删除未使用的代码,Resource才会成为无用的,从而被清除掉。
添加shrinkResources
true
属性在你的 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指定预约义的版本资源[issue 70869]
若是您但愿保留或丢弃特定的资源,请在项目中建立一个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种国家语言,而项目依赖了v七、v4等其余support包里面包含20种国家语言,那么咱们能够经过resconfigs 删除剩余的可替代资源文件,这对于咱们APK大小可减小了很多,
如下代码说明了如何限制你的语言资源,只是英语和法语:
android { defaultConfig { ... resConfigs "en", "fr" } }
像上面那样经过resconfig属性指定的语言。未指定的语言的任何资源都被删除。
一样的图片资源咱们也能够这么作,例如:咱们提供一套xxhdpi的图片资源,其余的都过滤清除掉,这样大量清除了support,其余第三方library的资源文件,关于这个待会咱们在后面会说。
默认状况下,Gradle也将同名的资源,好比相同的名字,多是在不一样的资源文件夹下,这样子不能经过shrinkResources属性来去除。
只有当两个或多个文件共享相同的资源名称、类型和限定符时才发生资源合并,关于资源的合并优先级以下:
Gradle 主要从如下位置合并资源:
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
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
如今须要知道为何@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.
那么这种状况咱们如何解决呢,能够经过如下方法来清理:
利用Lint找出未使用的资源并清理掉
在Android Studio中打开“Analyze” 而后选择”Inspect Code…”,范围选择整个项目,而后点击”OK”
到这里APK的大小又小了很多。
虽然咱们上面很好的使用了resource shrinker
能够回收一些未使用的资源(v七、v四、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 只支持如下类型:
使用新的APK Splits
,构建同一个应用程序的hdpi版本和mdpi版本,可以共享不少的任务 (如 javac,dx,proguard)。此外,它会被认为是一个单一的variant,而且同一个测试程序将会被用来测试每个多APK。
android { ... splits { density { enable true exclude "ldpi", "tvdpi", "xxxhdpi" compatibleScreens 'small', 'normal', 'large', 'xlarge' } }
构建完成后能够在out/apk/目录下看到多个版本的APK
android { ... splits { abi { enable true reset() include 'x86', 'armeabi-v7a', 'mips' universalApk true } } }
例如:咱们项目主要提供xxhdpi的图片资源,而项目中引用到了不少第三方库(v七、v四、google Service 等Libarry资源)咱们没法使用到,那么咱们能够经过这种方法来去除那些资源。这样咱们的APK又减少了很是多。
Multiple APK Support
是一个在Google Play,能够发布不一样的应用程序,分别针对不一样的设备配置特征。每一个APK是一个完整的、独立的应用程序版本,但他们分享在Google Play相同的应用程序清单,必须共享相同的包名和与签名。Google Play 会自动给你匹配相应的APK,这样咱们的APK 就能够是分不一样版本构建须要资源文件,从而减少APK的大小。
经过发布有多个APK,咱们能够:
支持不一样的CPU架构,每一个apk(如ARM、x86,MIPS等)的APK
更多相关信息请参考https://developer.android.com/google/play/publishing/multiple-apks.html#Concepts
目前咱们基于这个方案作了不一样屏幕的APK。
咱们能够在项目中使用资源动态加载形式,例如:表情,语言,离线库等资源动态加载,减少APK的大小。
将来对于一些独立业务模块,能够作成插件化动态加载,用户须要使用时,只需下载少部分插件。
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
关于APK瘦身就咱们就总结到这了,应该还有不少更好的方案,如有错漏,欢迎补充。
更多Android 之美,请阅读《Android 之美 从0到1 – 高手之路》系列文章
水平有限,如有错漏,欢迎指正,批评,如需转载,请注明出处–http://blog.csdn.net/vfush,谢谢!
做者:fuchenxuan
出处:http://blog.csdn.net/vfush
欢迎访问个人我的站点:http://fuchenxuan.cn
转载请注明出处–http://blog.csdn.net/vfush