Android有效地减少方法数

来源:互联网 发布:淘宝网夏秋棉麻连衣裙 编辑:程序博客网 时间:2024/06/03 13:09

原文链接http://jeroenmols.com/blog/2016/05/06/methodcount/

由于绿地项目是个罕见的工程,你有机会继承了一个陈旧代码库。如果你跟我一样幸运,代码库已经有超过65k个方法,导致构建时间令人烦躁的长。

今天,我会展示如何可视化你当前的方法数,并掌握哪些库占据了最大部分。接着就是减少方法数,并一劳永逸的移除讨厌的multidex解决方案。

可视化方法数

可视化方法数的最简单和最吸引人的方式(恕我直言)是使用方法Dexcount Gradle Plugin。应用到项目是很容易,只要在根build.gradle中添加classpath依赖,并在App的build.gradle中应用这个插件。

// root build.gradle filebuildscript {    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就在终端中打印出当前的方法数。

并在输出文件夹中生成一个交互图像报告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输出中删除所有干扰,排序并挑出所有唯一的引用。

接着,我创建一个简单的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找到我,或给我柳岩!

0 0
原创粉丝点击