记录思考

Android 编译时间太长的问题

Gradle 简直就是一个魔鬼,在使用 Android Studio 之前,我一直使用的是 eclipse 进行 Android 开发的,那会还在 real 的时候,即使在 T400 笔记本上,还是勉强可以工作的。

Gradle 简直就是一个魔鬼,在使用 Android Studio 之前,我一直使用的是 eclipse 进行 Android 开发的,那会还在 real 的时候,即使在 T400 笔记本上,还是勉强可以工作的。

后来换了工作,第一次接触 Android Studio,第一次接触 Gradle,简直就是折磨啊。这几年慢慢也开始熟悉这个谜一般的工具了。虽然现在对 Gradle 还是感觉很陌生,很多细节还是不了解,但还是想分享一下我知道的一些东西。

刚开始使用 Gralde 的时候,总是莫名的长时间转圈,也不知道它在干什么,后来才知道你在准备一些东西,下载一些必要的 jar 包,类库。第一次编译成功后,以后就快了。换了新的工作,搞好开发环境后,就是开始投入到工作中,没有仔细再去研究分析这个破玩意,能工作就好,别影响我做业务。

随着项目越来越大,编译的时间越来越长,已经到了很难忍受的地步了。我把开发用的台式机换成了 Linux 系统,编译的时间还算可以接受,基本上在两分钟左右,而同时的 windows 系统,四五分钟算是比较普遍的,更长的编译时间也是有的。

网上有很多 gradle 配置脚本的方案,基本上大家该加的也都加了,但是时间还是没有降下来。这里也吐槽一下 gradle 脚本的配置,简直就是谜一般的配置,基本上只能通过搜索来解决。文档,呵呵,只能上官网慢慢找吧。基本上就是面对着 gralde 脚本,根本不知道自己该做什么,能做什么,只能复制粘贴别人的。

不再重复别的配置,因为都可以在网上找到,说一下我自己的一些经验。

maven 私服

对于一个 Android 团队来说,自己搭建一个 maven 私服还是很有必要的。

我们团队刚开始是没有 maven 代理的,每个新员工入职的时候,都和我一样,有一个难忘和难熬的第一次编译,第一次编译成功后,往后就好了,大家也都把心思放在业务项目上了,直到有人引入新的依赖的时候。当其他小伙伴第一次拉新代码编译的时候,也需要远程的去下载新的依赖库,而这个过程又特别漫长。以至于后来,我们每次集成的时候,如果有新的依赖引入,都会相互打声招呼。这样的状态维持了很长一段时间,因为大家都在赶业务。

后来公司组建了一个架构小组,这个小组不需要负责具体的业务项目,我就是在这个小组里。对于架构组,我们的服务对象就是这六个业务的开发小组,我们需要给他们提供更好的服务,包括开发环境。于是我决定搭建了一个 maven 私服,配置好服务器,修改一下 Gradle 脚本,在悄无声息中就用上了。这样的一个服务,对于很多开发的小伙伴来说可能没有感知,因为对于他们来说,只是在平时他们就不太关注的 gradle 脚本上修改了几行代码而已。但是我却在一次事故中,发现了它确实很有用。公司搬家的时候,服务器进行了重启,而我申请的服务器我自己维护,没有给 maven 服务器加上自动重启服务,以至于第一次使用 Jenkins 打包的时候,用了很长的时间,通过看 Jenkins 的日志可以看出,因为 maven 服务器挂了,所有的依赖都需要从远程下载,所以才导致打包的时间比平时多了很多。重启服务之后,一切都回到原来的状态。

团队内搭建 maven 私服有两个重要的好处,一个就是通过 maven 代理,可以减少从远程仓库下载的时间。只要第一个人通过 maven 代理服务器下载了依赖,第二个人就可以直接从 maven 代理服务器下载了,而 maven 服务器在自己的内网,速度很快的,分分钟的事。

maven 私服的第二个重要作用就是,它是组件化的一个必要条件。我们要搞组件化,就需要一个 maven 私服,虽然没有也可以搞。

组件化

我们经常在两个场合下会讨论组件化,一个是业务解耦的时候,一个就是减少编译时间的时候。

当我们的项目越来越庞大的时候,业务解耦就变成了一个很有必要的事情。理想情况下,我们希望每个业务都像一个个组件似的,独立维护,编译,打包,然后在合并到一起,进行最后的发布。

刚才说 maven 的时候,也说到了组件化。我们发现 gradle 在编译的时候,有两个 task 消费的时间比较多,一个是 compileJavaWithJavac 一个 transformClassesWithDex 。第一个其实就是 javac 编译,把 java 文件编译成 class 文件。要减少 javac 的编译时间,就是需要减少 java 代码的数量。其中的一个方法就是提前把一些 java 代码编译好了,以 jar 包或者 aar 包的形式引入进来,就像第三方库一样。

当一个项目很大的时候,肯定是因为有很多不同的业务,我修改 A 业务的一个页面,但是我需要编译 B,C,D ... XYZ,肯定很不爽。如果我们把每个业务都可以单独打成一个 aar 包,那么就可以省下很多时间了。当然这样做的好处也不仅仅是为了减少编译时间那么简单了。我们要做的其实是业务解耦。

当然组件化也没有那么简单,需要我们做很多前置的工作,以及做好业务的拆分,不仅仅是技术而已。

不管怎么说,组件化确实可以减少编译的时间,但我们做组件化肯定不只是为了减少编译时间。

注解

Android 开发发展了那么多年,很多面向切面编程也被搬到了 Android 开发来了。这是一个很好的选择,面向切面编程可以大大的减少代码的复杂程度,让代码看上去更加漂亮一些,简洁明了才是利于维护的代码,才是好代码。

大部分运用了面向切面编程的库,基本上都会在编译的时候生成一些代码,然后在运行的时候,通过反射动态加载,运行。其中编译时期生成代码,就会产生新的 java 类,也就增加了 javac 的时间。这个时间是没法减少的。Bufferknife 就是这样的库,我们在编写代码的时候,代码量看上去少了很多,但是在编译前,他会帮我们生成很多代码,这些代码我们并没有注意,其实它们在 build 目录下,最终会和我们的代码一起编译,然后打到 apk 中。

这么说,我们是不是应该减少这类库的使用呢,这样就可以减少编译时间了。其实并不是,开发效率才是我们追求的,至于编译时间,我们总是有其他办法来解决它的,是吧。所以,这类框架、库,该用还是用,而且应该推荐用。

其它我不知道的手段

手段肯定是有的,对于 Android 开发来说,涉及到知识面还是很广的,也很有深度的。在加快编译打包速度上,我相信一定会有很快速的方式的,比如 Android Studio 的 Instant Run。我觉得还可以从 Gradle 插件入手,我相信一些规模比较大的团队,肯定会有人专门搞 gradle 插件。解决的思路,还是刚才说的 javac 和 class2dex 这两个步骤最耗时,javac 可以通过缓存 classes 文件,class2dex 可以通过插桩的方式,和热修复一个思路。

- EOF -

本文链接 https://spacepage.top/articles/2018.08.10-14_50_08-Android-Build-Slow.html,欢迎转载,转载请注明出处。