Android有效地减少方法数
2016-06-13 13:38
344 查看
原文链接http://jeroenmols.com/blog/2016/05/06/methodcount/
![](http://jeroenmols.com/img/blog/methodcount/methodcount.png)
由于绿地项目是个罕见的工程,你有机会继承了一个陈旧代码库。如果你跟我一样幸运,代码库已经有超过65k个方法,导致构建时间令人烦躁的长。
今天,我会展示如何可视化你当前的方法数,并掌握哪些库占据了最大部分。接着就是减少方法数,并一劳永逸的移除讨厌的multidex解决方案。
执行一个正常的工程构建
![](http://jeroenmols.com/img/blog/methodcount/methodcount_output.png)
并在输出文件夹中生成一个交互图像报告
![](http://jeroenmols.com/img/blog/methodcount/dexcount-googleplayservices/debugChart/index.html)
点击上图来看看效果
使用图形化表示应用中左右包的方法数,很方便就可以找出那个库正在消耗宝贵的方法数。一些常见的嫌犯是单独的大型库,如Guava和未拆分的Google play服务。在下节将看到我们能对这些库做什么。
替换应用中存在的库是非常具有挑战性,几乎不能的。
很多开发者使用巨大的库而只实现简单的事情。(
幸运的是,这有一个非常棒的网址methodscount.com可在开始使用库之前告诉你方法数。这可以真正的帮助你的应用避免使用”大库”却仅带来有限的收益。
每个库都有各自的优势和功能,所以明智的选择和平衡需要的功能与方法数带来的影响。
话虽这么说,替换现有库是令人难以置信的困难,并且短期内不可行。后面,我将建议一种折中方案来减少现有库的方法数。
嗯,这使可能的,但你不行手动指定你要使用库的哪部分!这是因为Proguard在库剔除过程中不知道应用需要那些方法的上下文。
让我们以我最近开始工作的工程为例。Guava在整个工程中广泛使用,使得清除它很困难/有风险。但由于庞大的方法数,我们不断的与65k方法数限制勾搭,并不得不启用multidex。
![](http://jeroenmols.com/img/blog/methodcount/dexcount-guava/debugChart/index.html)
首先你需要知道应用真正使用了库的哪部分。可在
该命令会检索所有包含库前缀引入语句(对于Guava,前缀是
![](http://jeroenmols.com/img/blog/methodcount/grep_output.png)
接着,我创建一个简单的Proguard配置,该配置保存所有顶级包名,不适用任何混淆或优化。
现在我们适用一个小Gradle脚本,该脚本拿库作为输入,对库执行Proguard,并生成一个新库。如果你感兴趣,我建议你看看源码,它是基于@mr_ligi写的脚本。这个Gradle脚本输出一个压缩版本的库,可将库拷贝到工程的libs文件夹。
看一下我们的例子,这个过程节省了4000个方法,我们距dex方法数限制只差一点了。
![](http://jeroenmols.com/img/blog/methodcount/dexcount-packageshrink/debugChart/index.html)
回到画板,因为这使远远不够的!结果发现
当试图在应用中使用库时,更积极的方法很有可能导致编译错误,所有我不得不遍历并添加额外的类,直到解决了所有的错误。由此产生的Proguard配置如下:
结果接近减少了4500个方法,总计删除了8500个方法!
![](http://jeroenmols.com/img/blog/methodcount/dexcount-agressiveshrink/debugChart/index.html)
进一步来说,我们的构建时间,不仅减少了,因为不需要multidexing了,也是因为编译器现在处理的代码更少了。
显然,你的里程取决于你选择什么库。对于低耦合,高聚合的库(例如Guava),这种技术工作的相当好,但对其他库可能就没啥效果。
免责声明
上面展示的技术要慎用!由于不依赖Proguard剔除那些不需要,我们现在要纯手工操作,这很容易出错.
对于已有工程,总试图用替代品的更换现有多方法的库。如果这是不可行的,并且你觉着使用Proguard很舒适,可以使用后者预处理和收缩现有库。
本文整合的一个基本例子可在GitHub中获取到。
你可在twitter上@molsjeroen找到我,或给我柳岩!
![](http://jeroenmols.com/img/blog/methodcount/methodcount.png)
由于绿地项目是个罕见的工程,你有机会继承了一个陈旧代码库。如果你跟我一样幸运,代码库已经有超过65k个方法,导致构建时间令人烦躁的长。
今天,我会展示如何可视化你当前的方法数,并掌握哪些库占据了最大部分。接着就是减少方法数,并一劳永逸的移除讨厌的multidex解决方案。
可视化方法数
可视化方法数的最简单和最吸引人的方式(恕我直言)是使用方法Dexcount Gradle Plugin。应用到项目是很容易,只要在根build.gradle中添加classpath依赖,并在App的build.gradle中应用这个插件。// root build.gradle file buildscript { repositories { jcenter() // or mavenCentral() } dependencies { classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.5.0' } }
// app build.gradle file (apply AFTER Android plugin) apply plugin: 'com.getkeepsafe.dexcount'
执行一个正常的工程构建
./gradlew assembleDebug就在终端中打印出当前的方法数。
![](http://jeroenmols.com/img/blog/methodcount/methodcount_output.png)
并在输出文件夹中生成一个交互图像报告
build/outputs/dexcount/debugChart:
点击上图来看看效果
使用图形化表示应用中左右包的方法数,很方便就可以找出那个库正在消耗宝贵的方法数。一些常见的嫌犯是单独的大型库,如Guava和未拆分的Google play服务。在下节将看到我们能对这些库做什么。
减少方法数
选择合适的库
通常我推荐从不优化,除非你遇到了问题。但对于方法数,我真的建议在开始使用库前考虑方法数,有2个原因:替换应用中存在的库是非常具有挑战性,几乎不能的。
很多开发者使用巨大的库而只实现简单的事情。(
Strings.isNullorEmpty()还有谁?)注意,你不需要使用一个库干所有的事情!
幸运的是,这有一个非常棒的网址methodscount.com可在开始使用库之前告诉你方法数。这可以真正的帮助你的应用避免使用”大库”却仅带来有限的收益。
替换已存在的库
通常有多个库完成相同的功能。以图片加载为例:库 | 方法数 |
---|---|
Picasso 2.5.2 | 849 |
UIL 1.9.5 | 1206 |
Glide 3.7.0 | 2879 |
Fresco 0.9.0 | 12984 |
话虽这么说,替换现有库是令人难以置信的困难,并且短期内不可行。后面,我将建议一种折中方案来减少现有库的方法数。
执行Proguard
Proguard是个伟大的工具,可以从应用中剔除无用的代码,但通常只在发布构建时执行它来节省宝贵的构建时间。如果对你不是问题,你也可以在调试版本中启动Proguard:buildTypes { debug { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
减少库大小
如果替换现有库或者执行Proguard都不可行,那么事情就变得很有趣了。因为如果Proguard可以在发布版本时剔除代码,为什么不在使用它之前创建一个带更少方法的修改版的库呢?嗯,这使可能的,但你不行手动指定你要使用库的哪部分!这是因为Proguard在库剔除过程中不知道应用需要那些方法的上下文。
让我们以我最近开始工作的工程为例。Guava在整个工程中广泛使用,使得清除它很困难/有风险。但由于庞大的方法数,我们不断的与65k方法数限制勾搭,并不得不启用multidex。
首先你需要知道应用真正使用了库的哪部分。可在
src目录执行下面的命令,就可以得到了。
rep -roh . -e 'com.google.common.*' | sort | uniq
该命令会检索所有包含库前缀引入语句(对于Guava,前缀是
com.google.common),从grep输出中删除所有干扰,排序并挑出所有唯一的引用。
![](http://jeroenmols.com/img/blog/methodcount/grep_output.png)
接着,我创建一个简单的Proguard配置,该配置保存所有顶级包名,不适用任何混淆或优化。
-dontoptimize -dontobfuscate -keep public class com.google.common.base.** { public *; } -keep public class com.google.common.collect.** { public *; } -keep public class com.google.common.primitives.** { public *; } -keep public class com.google.common.util.** { public *; }
现在我们适用一个小Gradle脚本,该脚本拿库作为输入,对库执行Proguard,并生成一个新库。如果你感兴趣,我建议你看看源码,它是基于@mr_ligi写的脚本。这个Gradle脚本输出一个压缩版本的库,可将库拷贝到工程的libs文件夹。
看一下我们的例子,这个过程节省了4000个方法,我们距dex方法数限制只差一点了。
回到画板,因为这使远远不够的!结果发现
collect包有超过8000个方法,因此我决定只保持这个类而不是整个包。
当试图在应用中使用库时,更积极的方法很有可能导致编译错误,所有我不得不遍历并添加额外的类,直到解决了所有的错误。由此产生的Proguard配置如下:
-dontoptimize -dontobfuscate -keep public class com.google.common.base.** { public *; } -keep public class com.google.common.collect.Sets -keepclassmembers class com.google.common.collect.Sets** { *; } -keep public class com.google.common.collect.Collections2 -keepclassmembers class com.google.common.collect.Collections2** { *; } -keep public final class com.google.common.collect.Lists -keepclassmembers class com.google.common.collect.Lists** { *; } -keep public final class com.google.common.collect.Iterables -keepclassmembers class com.google.common.collect.Iterables** { *; } -keep public class com.google.common.collect.ImmutableList.** { public *; } -keep public class com.google.common.io.CharStreams { public *; } -keep public class com.google.common.collect.HashMultiset -keepclassmembers class com.google.common.collect.HashMultiset** { *; } -keep public class com.google.common.collect.HashBiMap -keepclassmembers class com.google.common.collect.HashBiMap** { *; } -keep public class javax.annotation.Nullable.** { public *; } -keep public class com.google.common.util.** { public *; } -keep public class com.google.common.primitives.** { public *; }
结果接近减少了4500个方法,总计删除了8500个方法!
进一步来说,我们的构建时间,不仅减少了,因为不需要multidexing了,也是因为编译器现在处理的代码更少了。
显然,你的里程取决于你选择什么库。对于低耦合,高聚合的库(例如Guava),这种技术工作的相当好,但对其他库可能就没啥效果。
免责声明
上面展示的技术要慎用!由于不依赖Proguard剔除那些不需要,我们现在要纯手工操作,这很容易出错.
总结
击中65k方法数限制是真实的痛苦,但明智的选择使用哪个库可以让你走得更远。幸运的是像Dexcount Gradle Plugin和methodscount.com这样伟大的工具可以帮你做出这些决定。对于已有工程,总试图用替代品的更换现有多方法的库。如果这是不可行的,并且你觉着使用Proguard很舒适,可以使用后者预处理和收缩现有库。
本文整合的一个基本例子可在GitHub中获取到。
你可在twitter上@molsjeroen找到我,或给我柳岩!
相关文章推荐
- Android入门-SayHello及其多种实现方法
- 菜逼的Android旅程
- AndroidStudio 错误
- Android 开发注意事项
- Android异步任务:AsyncTask 和 Handler+Message详解
- Android 自定义Dialog(自定义主题、自定义布局
- Android Dialog 去黑边的方法
- android dialog 对话框显示位置
- android2.3.4----3.关于notify
- Android入门--Menu的基础应用
- 解决Android Studio中layout编辑窗不能预览Material Design的控件问题
- android2.3.4----2.关于framebuffer
- android2.3.4----1.ubuntu12.04 64位编译android2.3.4
- android 版本更新
- 安卓播放声音同时震动的效果实现
- Android 热修复-AndFix
- Android系统中自定义按键的短按、双击、长按事件
- Android中的Animation动画(xml)
- Android fill_parent、wrap_content和match_parent的区别
- Android 时区设置以及设置系统属性的分析