Gradle打包优化

3,020 阅读9分钟

打印k任务耗时命令

第一步我们运用命令来打印当前app build过程中耗时任务有哪些?

gradlew clean
gradlew --profile --offline --rerun-tasks assembleNeibuChannels

首先我们每次分析之前每次都需要执行clearn task,以便于排除其干扰项. 然后我们执行第二行的命令进行分析

  • --profile 启用分析
  • --offline 禁止 Gradle 提取在线依赖项。这样可以确保因 Gradle 尝试更新依赖项而导致的任何延迟都不会干扰您的分析数据。您应该已将项目编译一次,以确保 Gradle 已经下载和缓存您的依赖项。
  • --rerun-tasks 强制 Gradle 重新运行所有任务并忽略任何任务优化

分析结果图

  1. Summary

  2. Configuration

  3. Dependency Resolution

  4. Task Execution

分析并优化任务记录

由上面几幅图片我们可以看出来任务总耗时3m16.03s, 其中配置阶段耗时0.3s,解决依赖耗时0.622s,执行task耗时.由于task耗时比较严重,那么我们重点优化项应该在task上.

  • 查看比较耗时的任务有哪些?(此处忽略耗时在1s以下的任务,时间太少没必要优化)

  • 上图给我们展示了所有耗时超过一秒的任务(按照用时从多到少排序),我们把它重新分组排序,并进行标号.
任务 耗时
1 :app:transformClassesWithDexBuilderForNeibu 42.399s
2 :app:transformClassesWithGrowingioForNeibu 13.270s
3 :app:transformClassesWithFastClickTransformForNeibu 12.866s
4 :app:transformClassesWithMultidexlistForNeibu 7.179s
5 :app:transformDexArchiveWithDexMergerForNeibu 5.820s
6 :app:transformNativeLibsWithMergeJniLibsForNeibu 4.295s
7 :app:transformResourcesWithMergeJavaResForNeibu 3.050s
8 :app:compileNeibuKotlin 29.671s
9 :app:compileNeibuJavaWithJavac 19.072s
10 :app:packageNeibu 17.763s
11 :app:mergeNeibuResources 13.045s
12 :app:uploadNeibuSymtabFile 9.199s
13 :app:assembleNeibuChannels 5.959s
14 :app:processNeibuResources 3.745s
15 :app:greendao 3.470s
16 :app:greendaoPrepare 1.567s

transform优化

  1. 去掉开发过程中不必需的transform.
# 内部环境 true GrowingIO 参与编译,false 不参与编译
gioenable = false
#false表示不编译该插件,加快编译速度
fastClickEnable=false
#false表示不编译该插件,加快编译速度
methodTimeEnable=false
#false表示不编译该插件,加快编译速度
kLogPluginEnable=false
  1. 统计结果
序号 优化项 首次运行 第二次 第三次 第四次
无优化 3m16.03s 2m26.00s 2m19.31s 2m20.95s
优化1 关闭非必要transform开关(控制台运行) 1m44.08s 1m45.28s 1m41.62s

经过上面的优化我们在开发过程中关闭transform开关可以节省每次编译时长x s

kotlin初次编译优化

compileNeibuKotlin此行是编译kotlin代码的过程.但是此过程却耗费了我们将近30s的时间.多数情况下甚至更多经过实测是(29-37s).一般会超过32s.那么如何优化这30s呢?我进行如下尝试

  1. 优化1 升级kotlin及插件版本 原来版本1.3.41 -> 升级到 1.3.61

工程build.gradle中buildscript->dependencies配置

classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"

工程app模块下dependencies配置

implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.61'
  1. 优化2 升级kotlin jvm版本1.6->1.8(方式如下)

3. 优化3 关闭report开关(方式如下)

优化项 关闭studio的首次(rebuild) 第二次rebuild 第三次rebuild 结果
无优化 33s276ms 16s80ms 13s897ms
优化1 30s987ms 19s984ms 15s144ms 失败(二次运行时间反而更长)
优化2 30s689ms 17s317ms 30s34ms 失败
优化3 33s195ms 16s994 34s307ms 失败

由上表经过优化1(简称1),2,3,的测试,结果说明,在clean项目的情况下,无论是升级kotlin及插件版本和升级target jvm version或者关闭警告并行编译,都无法明显加快kotlin编译器运行速度.此处需要后续深入研究

kotlin 二次运行优化

开启kotlin并行编译开关(方式如下)

//开启kotlin的增量和并行编译
kotlin.incremental=true
kotlin.incremental.java=true
kotlin.incremental.js=true
kotlin.caching.enabled=true
kotlin.parallel.tasks.in.project=true //开启kotlin并行编译

开启开关每次rebuild运行耗时表

优化项 关闭studio的首次(rebuild) 第二次rebuild 第三次rebuild 结果
添加配置 14s48ms 29s844ms 30s40ms 失败(无提升)

开启开关多次运行编译耗时表

优化项 第一次(clean后第一次运行,后面不clean了) 第二次 第三次 结果
无优化(修改java代码) 38s813ms 19s66ms 19s312
无优化(修改kt代码) 32s482ms 17s480 14s202ms
增加配置后(修改java代码) 23s376ms 7s526ms 5s739ms 首次运行和非首次运行都能得到明显优化
增加配置后(修改kotlin代码) 39s605ms 2s486 2s507ms 二次运行能得到更明显优化

由上面表格可知,在增加kotlin 增量编译缓存及开启并行编译的配置后. kotlin在首次编译及二次编译后运行速度均有提升.

去bugly插件

优化项 命令行编译1 命令行编译2 命令行编译3 结果
无优化 2m58.25s 2m18s 2m7s
去除插件 2m48.00s 2m10s 2m5.62s 整体时间变短

去掉开发过程中不用的插件,并打开即时运行

  1. 工程下 gradle.properties 里增加
isDebug = true
  1. build.gradle脚本根据isDebug判断,不需要则不执行相应的classpath 和 apply等代码
if (!Boolean.valueOf(project.properties.isDebug)){
    
}
例如:
1. 工程build文件 避免工程引入插件库
if (!Boolean.valueOf(project.properties.isDebug)){
            println "isDebug=false0"
            classpath 'com.koo.klogplugin:klogplugin:1.0.5'
            classpath 'com.k.plugin_methodtime:methodtimeplugin:2.0.8'
            classpath 'com.k.plugin_fastclick:fastclick-plugin:2.0.1'
            classpath 'com.growingio.android:vds-gradle-plugin:autotrack-2.8.4'
            classpath 'com.tencent.bugly:symtabfileuploader:2.1.2' // Bugly符号表
            classpath 'com.tencent.bugly:tinker-support:1.2.0'
            classpath 'com.meituan.android.walle:plugin:1.1.6'
        }else{
            println "isDebug=true0"
        }
2. appBuild文件 避免工程应用插件库及其配置
if (!Boolean.valueOf(properties.isDebug)){
    println "isDebug=false1"
    apply from: 'config/bugly.gradle'
    apply plugin: 'com.growingio.android'
    apply plugin: 'com.ko.klogplugin'
    apply plugin: 'com.k.methodtimeplugin'
    apply plugin: 'com.k.fastclickplugin'
    apply plugin: 'walle'
    methodTimeConfig {
        milliSeconds 200
    }
    apply from: 'config/bak_mapping.gradle'
    apply from: 'config/tinker-support.gradle'
}else{
    println "isDebug=true1"
}
if (!Boolean.valueOf(project.properties.isDebug)){
        println "isDebug=false2"
        walle {
            // 指定渠道包的输出路径
            apkOutputFolder = new File("${project.buildDir}/tinkerApk/" + releaseTime() + "/")
            // 定制渠道包的APK的文件名称
            apkFileNameFormat = "k_v\${versionName}_" + releaseTime() + "_\${buildType}_\${channel}.apk"
            // 渠道配置文件
            channelFile = new File("${project.getProjectDir()}/channel")
        }
    }else{
        println "isDebug=true2"
    }
  1. 打开instant run开关

4. clean运行分析

优化项 inva重启->clean->run (修改一行java)run2 (修改一行java)run3 结果
isDebug = false 2m20s 1m3s947 1m0s192
isDebug = true 2m28s960 48s363 43s846 明显加快编译速度

Gradle,build时常见的task

:app:clean
该步骤主要是清理上次编译的遗留产物,删除module下的build文件夹内所有内容
:app:preDebugBuild
新建build文件夹,内部新增intermediates/incremental文件夹,针对各个不同的buildType,新建对应的文件夹,内部新增zip-cache文件夹,内容为空
:app:checkDebugManifest
AndroidManifest检查,貌似不包括内容检查,故意写错也不会报错
:app:preBt1Build
看起来没有做特别的事情……
:app:preBt2Build
看起来没有做特别的事情……
:app:preReleaseBuild
看起来没有做特别的事情……
:app:prepareBrowserProfessionalLibrary
将工程中用到的某个aar拷贝并解压缩到module下的build/intermediates/exploded-aar/**/目录下,内容已经解压缩了
:app:prepareComAndroidSupportAnimatedVectorDrawable2340Library
将support对应的包解压缩到exploded-aar文件夹下,与上一个逻辑基本相同
:app:prepareComAndroidSupportAppcompatV72340Library
与上述一致
:app:prepareComAndroidSupportDesign2340Library
与上述一致
:app:preDebugAndroidTestBuild
_没有看到对应的结果,使用的工程里面没有test吧,估计_
:app:prepareComAndroidSupportMultidex101Library
对multidex的支持,在exploded-aar中新建对应文件
:app:prepareComAndroidSupportRecyclerviewV72340Library
同上support操作
:app:prepareComAndroidSupportSupportV42340Library
:app:prepareComAndroidSupportSupportVectorDrawable2340Library
:app:prepareComBrowserCommomCore720Library
将对应aar解压缩到对应位置,如上述
*   :app:prepareDebugDependencies
    _没有看到对应的变化,应该是没有对应的配置导致的_
*   :app:compileDebugAidl
    生成generated文件夹,下面对应有aidl文件夹,针对当前buildtype生成对应的文件夹
*   :app:compileDebugRenderscript
    generated文件夹下,生成res文件夹,针对对应的buildtype生成对应文件夹
*   :app:generateDebugBuildConfig
    generated/source文件夹下,生成buildConfig文件夹,针对对应的buildtype生成对应文件夹
*   :app:generateDebugResValues
    generated/res文件夹下,生成resValues文件夹,针对对应的buildtype生成对应文件夹
*   :app:generateDebugResources
    _看起来没有新的文件生成……_
*   :app:mergeDebugResources
    merge资源文件,在intermediates下生成res文件夹以及blame文件夹,其中放置merge后的资源文件
*   :app:processDebugManifest
    将merge后的Manifest文件放在intermediates/manifests文件夹下
*   :app:processDebugResources
    处理资源文件,生成R.txt文件,同时也生成对应的multidex文件夹,内容为空
*   :app:generateDebugSources
    在generated文件夹下生成对应的R.java文件
*   :app:incrementalDebugJavaCompilationSafeguard
    在incremental-safeguard目录下生成tag.txt,标识已经执行过task
*   :app:compileDebugJavaWithJavac
    intermediates下生成classes文件夹,以及对应的dependency-cache文件夹,classes文件夹中包含之前已经解压的各个aar文件中的类,但是不包含libs下的jar包中的内容;_同时还会生成一个tmp文件夹,内容为空_;**目录下不包括libs下的jar包内容**
*   :app:compileDebugJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
*   :app:compileDebugNdk
    工程下没有对应的ndk编译,没有新内容增加
*   :app:compileDebugSources
    _看起来没有什么文件变化,不清楚这个阶段在做什么,可能是加入R文件参与编译吧_
*   :app:mergeDebugShaders
    _新建了一个shaders文件夹,但是内容为空,这块还需要看下_
*   :app:compileDebugShaders
    _没有文件变化,不清楚是干嘛的_
*   :app:generateDebugAssets
    在generated文件夹下生成assets文件夹,但是内容为空
*   :app:mergeDebugAssets
    在intermediates下生成assets文件夹,将其他module/aar中的assets文件拷贝过来

*   :app:transformClassesWithTransformImplForDebug
    由于工程gradle实现了transform的plugin,文件被拷贝到对应的目录,对应的class文件也已经被操作过了;对应的jar包也被操作过了。jar目录中的文件数量是libs下和aar下的所有jar包的合集。folder下的目录也无缺失

*   :app:transformClassesWithJarMergingForDebug
    将所有jar包merge到一起,在transform的jarMerging下,生成一个combined.jar,其他地方未见对应jar包
*   :app:transformClassesWithMultidexlistForDebug
    在multi-dex文件夹下生成对应buildtype的componentClasses.jar\components.flags\maindexlist.txt文件,_由于demo工程较小,因此未见多个jar包_
*   :app:transformClassesWithDexForDebug
    新增一个pre-dexed文件夹,内部为空
    transform内部新增一个dex文件夹,内部存在一个classes.dex文件
*   :app:mergeDebugJniLibFolders
    新增一个jniLibs文件夹,内部将工程使用的所有so都放置在一起
*   :app:transformNative_libsWithMergeJniLibsForDebug
    将所有so库都放在transform目录下,按照架构区分放置
*   :app:processDebugJavaRes UP-TO-DATE
    生成aapt-temp文件夹,内容为空
*   :app:transformResourcesWithMergeJavaResForDebug
    在transform目录下生成mergeJavaRes文件夹,内部有一个main.jar
*   :app:validateSigningDebug
    应该是在做一些校验操作,没有明显的文件变化
*   :app:packageDebug
    在output文件夹中生成app-debug.apk,此时app已经可以安装了(这里忽略了签名的过程)
*   :app:assembleDebug
    _没看出来文件有啥具体变化……_

编译时去掉指定任务

如果确实在build时不想运行某些脚本可以执行此方法(放到task插件定义apply的build文件中).那么程序在build时则不会执行enable=false的task.

注意这样设置会导致程序在任何地方都不会被编译

tasks.whenTaskAdded { task ->
    if (task.name.contains("greendao")) {//如果没有修改数据库相关字段,则可以禁止编译此项
        task.enabled = false
    }
}

一些官网优化编译速度的建议

developer.android.com/studio/buil…

Studio升级建议

www.cnbeta.com/articles/so…