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

Android有效地减少方法数

2016-06-13 13:38 344 查看
原文链接http://jeroenmols.com/blog/2016/05/06/methodcount/



由于绿地项目是个罕见的工程,你有机会继承了一个陈旧代码库。如果你跟我一样幸运,代码库已经有超过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
就在终端中打印出当前的方法数。



并在输出文件夹中生成一个交互图像报告
build/outputs/dexcount/debugChart




点击上图来看看效果

使用图形化表示应用中左右包的方法数,很方便就可以找出那个库正在消耗宝贵的方法数。一些常见的嫌犯是单独的大型库,如Guava和未拆分的Google play服务。在下节将看到我们能对这些库做什么。

减少方法数

选择合适的库

通常我推荐从不优化,除非你遇到了问题。但对于方法数,我真的建议在开始使用库前考虑方法数,有2个原因:

替换应用中存在的库是非常具有挑战性,几乎不能的。

很多开发者使用巨大的库而只实现简单的事情。(
Strings.isNullorEmpty()
还有谁?)注意,你不需要使用一个库干所有的事情!

幸运的是,这有一个非常棒的网址methodscount.com可在开始使用库之前告诉你方法数。这可以真正的帮助你的应用避免使用”大库”却仅带来有限的收益。

替换已存在的库

通常有多个库完成相同的功能。以图片加载为例:

方法数
Picasso 2.5.2849
UIL 1.9.51206
Glide 3.7.02879
Fresco 0.9.012984
每个库都有各自的优势和功能,所以明智的选择和平衡需要的功能与方法数带来的影响。

话虽这么说,替换现有库是令人难以置信的困难,并且短期内不可行。后面,我将建议一种折中方案来减少现有库的方法数。

执行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 Pluginmethodscount.com这样伟大的工具可以帮你做出这些决定。

对于已有工程,总试图用替代品的更换现有多方法的库。如果这是不可行的,并且你觉着使用Proguard很舒适,可以使用后者预处理和收缩现有库。

本文整合的一个基本例子可在GitHub中获取到。

你可在twitter上@molsjeroen找到我,或给我柳岩!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: