android源码编译卡-AndroidStudio编译缓慢、卡顿、占用显存

至于编译速度的提升,我有一句话要说。 我认为受访者给出的一些答案不太适用。 虽然应该从gradle入手,但是如果有不合适的地方,请轻喷。 也欢迎任何问题。 信息。

建议在终端中执行我下面提到的所有步骤。 在终端中编译有很多优点:

可以观察整个编译过程,有助于理解gradle创建过程; 可以看到编译过程中哪些任务耗时较长,可以针对编译慢的问题对症下药; 您可以随时停止编译。 如果卡在某个阶段,ctrl+c可以随时停止编译,在AndroidStudio中停止编译,基本上十有八九会失败; 因为是在终端,所以对AndroidStudio影响很小,基本不会导致AndroidStudio卡顿; 不会遇到各种AndroidStudio Class bug。

先说一下gradle的生命周期。 gradle建立一个项目主要分为三个部分(充分掌握下图,整个gradle创建过程十之八九就能明白):

<imgsrc=";data-rawwidth="1468"data-rawheight="786"class="origin_imagezh-lightbox-thumb"width="1468"data-original=";>

初始化阶段:主要是解析setting.gradle文件(因此有人提到减少setting.gradle的模块数量,这是很合理的,而实际操作过程很有限,原因会在最后大致解释); 读取配置阶段:主要分析所有项目下的build.gradle文件,包括rootProject及其他子项目(子项目),检测句型,判断任务的依赖关系,完善任务的有向无环图,检测文件目录是否存在任务中引用的存在等(这一步也进一步验证了减少setting.gradle中的模块数量可以提高编译速度,因为减少一个模块会减少需要解析的build.gradle文件数量,并且该模块属于该模块的任务不会在步骤3.任务中执行,仍然是上面1中提到的问题,限制很少); 执行阶段:根据2中构建的有向无环图执行各个任务,而这一步基本上会占用整个编译过程90%以上的时间,特别是对于Android项目,将java转换为class

compileDebugJavaWithJavac/compileReleaseJavaWithJavac

并将类合并到dex中

transformClassesWithDexForDebug/transformClassesWithDexForRelease

这两个步骤需要很长时间。 第一步还可以,但是第二步就需要很长时间了。首先在gradle.properties中设置

org.gradle.jvmargs=-Xmx4096m //越大越好

,然后减少项目的build.gradle中android节点下的dexOptions配置,如下:

dexOptions {
    dexInProcess true
    preDexLibraries true
    javaMaxHeapSize "4g"//越大越好
    incremental true
}

明确了gradle的生命周期后,我们可以看出,提高编译速度的关键是从第三步开始。 事实上,减少setting.gradle中的模块数量也是必要的。 下面讲一下我们公司的做法。

对于项目插件重构,每个业务朋友只需要编译一个模块,基本解决了编译慢的问题(对于大多数不需要插件的同学,可以看下面的一些做法),首先, setting.gradle中的module只有自己开发的模块,执行阶段对应的任务也只是该模块的任务。 执行一次gradlebuild,我们都会发现,在这个过程中,虽然执行了多个打包任务,但是buildTypes中配置了多个编译打包类型。 默认有debug和release。 我们还可以自动配置其他类型,并且productFlavor中还有多通道,这样就会进行多次编译和打包。 正常开发过程中,只需要打开debug包进行调试,因此可以使用gradleassembleDebug,版本发布时使用其他方式打开多个通道。 包(如美团的解决方案tech.meituan.com/mt-apk-packaging.html); 由于主要的编译时间集中在gradle生命周期的第三步,我们可以把一些不相关的比如各种测试,各种lint等,在gradle中有这样的指令 -xlint 可以暂时禁用lint任务,-xtest可以禁用测试任务,事实上,对于稍微大一点的项目,lint也是非常耗时的。 事实上,您可以通过 gradle 脚本完全禁用 lint 和测试任务。 我也在一些Momo群里分享过相关代码,我不建议这样做,因为有时候lint和test也很有用。 ;gradle本身提供了一些命令参数来促进编译,比如--daemon,启用守护进程,--parallel,启用并行编译等。这个也可以在gradle.propertites中配置(用于的jvm显存)编译也可以在这里配置)。

自定义gradle编译流程。 借助官方API,您可以定制适合您的编译流程。 可以参考同流程的DynamicAPK/​​sub-project-build.gradleatmaster·CtripMobile/DynamicAPK·GitHub,其中有艺龙自己的整个编译过程,脚本本身很简单,只有两三百行代码总共。 里面提到的几点,现有的环境大致可以这样做(需要注意的是,如果项目中有交叉依赖,一定不要使用--parallel参数):

gradle assembleDebug --daemon --parallel -x lint -x test

,如果想直接在设备上安装,将assembleDebug替换为installDebug,assembleDebug可以简写为D,installDebug可以简写为iD。

最后说一下为什么减少setting.gradle中的模块数量确实可以促进编译,但是限制却很少?

首先我们来思考一下整个编译过程,首先分析gradle配置,构建任务依赖关系的有向图,然后执行各个模块的任务。 如果我们依赖maven,使用aar来替换模块(单指android库),如果我们要改变这个模块中的文件,那不是每次上传下载都要改一下,也许还可以吧,而且还有一个致命的问题:如果不更改版本号,SNAPSHOT在IDEA中往往无法工作。 这样改出来的东西就不会生效,而且要花很多时间才能解决这个问题。 不过有一个办法可以一定程度上解决这个问题,减少下面的脚本:

project.configurations.all(new Action() {
@Override
    void execute(Configuration files) {
        files.resolutionStrategy.cacheDynamicVersionsFor(5, TimeUnit.MINUTES)
        files.resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
    }
})

那么有人会问,在插件中,每个人开发一个模块,每个模块的维护都要打包上传到maven。 每次有改动android源码编译卡,哪怕是很小的改动,都需要上传一次。 你会遇到SNAPSHOT不起作用的问题。 哎,这个问题,我们公司维护了一个gradle插件,已经解决了。 至于解决办法,那是公司的最高机密android源码编译卡,我就不说了。

在那之后,还有一点。 相信大多数开发者通常都是开发单个模块,多模块的情况并不多。 因此,大多数依赖项基本上都是aar或jar,并且不存在库之类的东西。 转换为aar上传,所以有些受访者所说的完全没有意义,这也是为什么我说影响编译率的情况主要集中在gradle生命周期的第三阶段。 至于第三阶段的优化,看我内心的答案就可以了。