草庐IT

Android加快你的编译速度

JakeWharton 2023-09-14 原文

工欲善其事,必先利其器。如果每次运行项目都要花费5-10分钟,那人的心态都要崩了。

Gradle构建流程

Gradle 的生命周期可以分为大的三个部分:初始化阶段(Initialization Phase),配置阶段(Configuration Phase),执行阶段(Execution Phase)。

优化方案

从整体构建流程可以得知,我们整体上需要从三个方面进行优化:

  • 初始化速度优化
  • 配置速度优化
  • 执行速度优化
    其中执行的过程占比是最大的,所以重心放在执行速度优化上。
1.初始化速度优化

当组件化程度较高时,在开发某个特定功能过程中有些组件是不需要引入的,此时可以在setting.gradle中移除不需要引入的组件模块,可以减少初始化时间

2.配置速度优化

配置阶段主要是对各个build.gradle进行解析,因此可以注意以下几点:

按需引入模块,减少build.gradle的解析
build.gradle中尽量少做耗时操作,例如读取系统时间动态配置apk的名称组成
在开发阶段不是必要执行的任务,可以写判断避免这些任务的配置,例如一些字节码插桩,性能监控之类的。

  • 开启Configuration Cache

在任务执行阶段,Gradle提供了多种方式实现Task的缓存与重用(如up-to-date检测,增量编译,build-cache等)。

除了任务执行阶段,任务配置阶段有时也比较耗时,目前AGP也支持了配置阶段缓存Configuration Cache,它可以缓存配置阶段的结果,当脚本没有发生改变时可以重用之前的结果。

在越大的项目中配置阶段缓存的收益越大,module比较多的项目可能每次执行都要先配置20到30秒,尤其是增量编译时,配置的耗时可能都跟执行的耗时差不多了,而这正是configuration-cache的用武之地。

目前Configuration-cache还是实验特性,如果你想要开启的话可以在gradle.properties中添加以下代码:

# configuration cache
org.gradle.unsafe.configuration-cache=true
org.gradle.unsafe.configuration-cache-problems=warn
3.执行速度优化
  • 开启并行编译

开启后会并行执行多个任务,大幅度减少编译时间,只需要在gradle.properties中添加:

org.gradle.parallel=true
  • 增大编译内存

由于大家的电脑配置都不一样,因此具体设置多大内存需要根据个人情况进行合理配置,一般在gradle.properties里已经有相关配置,可以对该配置进行修改,例如

org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

同时在主工程模块的build.gradle中进行修改:

    dexOptions {
        javaMaxHeapSize "4g"
    }
  • 开启按需构建

对没有更改的模块不再进行编译,非常适合已经组件化的项目,在gradle.properties中添加:

org.gradle.configureondemand=true
  • 开启构建缓存

直接使用之前生成的缓存,不再进行构建,在构建时任务后面会显示FROM CACHE,在gradle.properties中添加:

org.gradle.caching=true
  • 开启增量注解编译

支持注解增量编译,不会重新触发编译(gradle高版本中需要移除),在gradle.properties中添加:

android.enableSeparateAnnotationProcessing=true
4.其他速度优化
  • 对AS进行配置

开启离线模式
开启离线模式后不会再开始的时候去检测依赖是否有更新,也不会去下载相关更新的依赖,首次构建不能开启,否则无法完成构建,后续构建可以开启,在某些情况下将大幅度改善编译速度,强烈推荐开发阶段使用。点击下图中的图标的按钮即可开启离线模式,有些版本显示为类似wifi的图标,再次点击取消离线模式:


  • 更改AS内存大小

点击AS的Help菜单项,选中Change Memory Settings选项。


  • KAPT 迁移到 KSP

注解处理器是Android开发中一种常用的技术,很多常用的框架比如ButterKnife,ARouter,Glide中都使用到了注解处理器相关技术。

但是如果项目比较大的话,会很容易发现KAPT是拖慢编译速度的常见原因,这也是谷歌推出KSP取代KAPT的原因。

  • 关闭R文件传递

在 apk 打包的过程中,module 中的 R 文件采用对依赖库的R进行累计叠加的方式生成。如果我们的 app 架构如下:

编译打包时每个模块生成的R文件如下:

1. R_lib1 = R_lib1;
2. R_lib2 = R_lib2;
3. R_lib3 = R_lib3;
4. R_biz1 = R_lib1 + R_lib2 + R_lib3 + R_biz1(biz1本身的R)
5. R_biz2 = R_lib2 + R_lib3 + R_biz2(biz2本身的R)
6. R_app = R_lib1 + R_lib2 + R_lib3 + R_biz1 + R_biz2 + R_app(app本身R)

1.关闭R文件传递可以通过编译避免的方式获得更快的编译速度
2.关闭R文件传递有助于确保每个模块的R类仅包含对其自身资源的引用,避免无意中引用其他模块资源,明确模块边界。
3.关闭R文件传递也可以减少很大一部分包体积与dex数量

从 Android Studio Bumblebee 开始,新项目的非传递 R 类默认处于开启状态。即gradle.properties文件中都开启了如下标记

android.nonTransitiveRClass=true
  • 开启Kotlin跨模块增量编译

使用组件化多模块开发的同学都有经验,当我们修改底层模块(比如util模块)时,所有依赖于这个模块的上层模块都需要重新编译,Kotlin的增量编译在这种情况往往是不生效的,这种时候的编译往往非常耗时。

在Kotlin 1.7.0中,Kotlin编译器对于跨模块增量编译也做了支持,并且与Gradle构建缓存兼容,对编译避免的支持也得到了改进。这些改进减少了模块和文件重新编译的次数,让整体编译更加迅速。

在 gradle.properties 文件中设置以下选项即可使用新方式进行增量编译:

kotlin.incremental.useClasspathSnapshot=true // 开启跨模块增量编译
kotlin.build.report.output=file // 可选,启用构建报告
  • Module源码转aar

随着业务量的增大,module的引入也会增多,每个module在编译的时候都需要花费一定的时间。把module转化成aar后就不再需要每次都进行编译或者取缓存,可以减少一部分时间。

参考:
https://mp.weixin.qq.com/s/q1zIhtTXvKubhg3HOG7T0Q
https://blog.csdn.net/Nbin_Newby/article/details/120439965

有关Android加快你的编译速度的更多相关文章

  1. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  2. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  3. .net - 是否有 Ruby .NET 编译器? - 2

    是否有适用于Ruby语言的.NETFramework编译器?我听说过DLR(动态语言运行时),这是否将使Ruby能够用于.NET开发? 最佳答案 IronRuby是Microsoft支持的项目,建立在动态语言运行时之上。 关于.net-是否有Ruby.NET编译器?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/199638/

  4. python - 使用 Python、Ruby 和 Perl 重新编译 MacPort 版本的 MacVim - 2

    关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭10年前。ImprovethisquestionLinux专家正在转向Mac(10.8)。因为我懒...我使用MacPorts安装MacVim。它似乎安装没有错误。我只需要mvim中的python、ruby和perl支持。$/opt/local/bin/mvim--version|egrep'patches|python|ruby|perl'Includedpatches:1-244,246-646+multi_lang-mzscheme+

  5. ruby - 为什么 `middleman serve` 有效,但是 `middleman build` 编译这个 Sass 失败? - 2

    当我刚刚运行middleman时服务,all.css编译得很好,只包含对+box-shadow(none)的调用:/*line1,/home/yang/asdf/source/stylesheets/content.css.sass*/div{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}但是当我构建网站时,我得到了这个Sass/Compass错误:$middlemanbuildSlim::EmbeddedEngineisdeprecated,itiscalledSlim::EmbeddedinSlim2.0

  6. java - Ruby 和 Java 的速度 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。在我在网上找到的每个基准测试中,Ruby似乎都很慢,比Java慢得多。Ruby的人只是说这无关紧要。您能举个例子说明RubyonRails(以及Ruby本身)的速度真的无关紧要吗?

  7. ruby - 有没有办法在 Ruby 中执行编译时类型检查? - 2

    我知道Ruby是动态和强类型的,但据我所知,由于每个参数缺少显式类型表示法(或契约),当前语法不允许在编译时检查参数类型。如果我想执行编译时类型检查,我有哪些(实际成熟的)选项?更新我的意思是类型检查类似于典型的静态类型语言。比如C。例如,C函数表示每个参数的类型,编译器检查传入的参数是否正确。voidfunc1(structAAAaaa){structBBBbbb;func1(bbb);//Wrongtype.Compiletimeerror.}作为另一个例子,Objective-C通过放置显式类型信息来做到这一点。-(id)method1:(AAA*)aaa{BBB*bbb=[[A

  8. ruby - Ruby 将来可以编译并更快吗? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭7年前。ImprovethisquestionC、Java、C#和Python都是从头编译的。感谢Facebook,PHP现在也可以编译并可以在HHVM上运行,从而提高程序的性能。Ruby不可编译并且比上述语言慢。Ruby有没有可能在未来被编译(就像PHP和HHVM一样)?或者可能有一些原因不能做到?

  9. ruby - 有没有办法让文件夹包含在生产构建中但不让 jekyll 编译它? - 2

    我认为最好的例子是images/文件夹或node_modules/用于将图像和依赖项包含在最终构建中,而无需花费很长时间编译。编辑:抱歉没有具体说明,但我很清楚keep_files和exclude两者都对我的情况没有帮助。exclude从编译和站点构建中排除文件夹和文件,并且每次都需要额外的流水线工具来手动移动它们。keep_files要求文件首先存在于最终构建中,这对于某些生产环境(GitHub的gh-pages等)是不可能的 最佳答案 你可能想看看这个:ExcludingadirectoryfromJekyllwatchP.S.

  10. ruby - 我如何将 Ripper 的 AST 重新编译回 Ruby 代码? - 2

    Ripper是Ruby1.9附带的解析库。它将Ruby代码转换为AST,如下所示:ppRipper.sexp("deffoo;yield:a;return1end")#=>[:program,[[:def,[:@ident,"foo",[1,4]],[:params,nil,nil,nil,nil,nil],[:bodystmt,[[:yield,[:args_add_block,[[:symbol_literal,[:symbol,[:@ident,"a",[1,16]]]]],false]],[:return,[:args_add_block,[[:@int,"1",[1,26]]

随机推荐