分享一个统计 Android 模块构建时长的脚本工具

背景

通常我们会遇到项目中存在很多模块的情况,每次构建都会花费好久,但是又不知道每个模块耗费多久,因此使用该工具可以很方便的检测,用来决定如何优化项目结构

实现效果

构建总耗时:6637ms 
app, 总耗时:4943ms 
xxx-util, 总耗时:90ms 
lib, 总耗时:148ms 
xxx_browser, 总耗时:22ms 
videoxxx, 总耗时:33ms 
xxxmodule, 总耗时:90ms 
xxx_message, 总耗时:69ms 
xxx_notchtoolslib, 总耗时:46ms 
xxx_zxing, 总耗时:27ms 
xxx_emojilib, 总耗时:36ms 
xxx_magicindicator, 总耗时:20ms 
xxx_uselogin, 总耗时:46ms 
xxxPlugin, 总耗时:92ms 
xxxcommonlib, 总耗时:53ms 
xxxmvvm, 总耗时:55ms 

这样很容易就能看到每个模块耗时多久。

使用源码方式

  1. 在 app/build.gradle 添加
...
apply from: "../buildTrace.gradle"
...

  1. 项目根目录下

buildTrace.gradle

import java.text.SimpleDateFormat

/**
 * 监控构建耗时
 *
 * 记录构建过程中的每个任务的耗时,并写入文件中 .build_history/buildTimeLog_yy_MM_dd_HH_mm_ss.log中
 */

class BuildTimeListener implements TaskExecutionListener, BuildListener {
    private final String BUILD_LOG_FILE_DIR = ".build_history"
    private final String BUILD_LOG_FILE_PATH = "buildTimeLog"
    private long taskStartTime;
    private long buildStartTime;
    private int taskCounts = 0;
    private StringBuilder sb = new StringBuilder()
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    private SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss")
    private SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS")

    private Map<String, Long> moduleTimeMap = new HashMap<>()

    BuildTimeListener() {
        buildStartTime = System.currentTimeMillis()
        sb.append("构建时间:" + sdf3.format(new Date()) + "\n")
        sb.append("开始构建----------------------\n")
    }

    @Override
    void buildStarted(Gradle gradle) {

    }

    @Override
    void settingsEvaluated(Settings settings) {

    }

    @Override
    void projectsLoaded(Gradle gradle) {

    }

    @Override
    void projectsEvaluated(Gradle gradle) {

    }

    /**
     * 构建完成回调
     * @param result The result of the build. Never null.
     */
    @Override
    void buildFinished(BuildResult result) {
        if(taskCounts<5) return
        long buildCost = System.currentTimeMillis() - buildStartTime
        sb.append("构建结束------------------" + sdf3.format(new Date()) + "----\n")
        sb.append("构建总耗时:" + buildCost + "ms \n")

        for (Map.Entry<String, Long> entry : moduleTimeMap.entrySet()) {
            sb.append(entry.key + ", 总耗时:" + entry.value + "ms \n")
        }


        //输出到文件
        String text = sb.toString()
        writeToFile(BUILD_LOG_FILE_PATH, text)
    }

    /**
     * 任务执行开始
     * @param task The task about to be executed. Never null.
     */
    @Override
    void beforeExecute(Task task) {
        taskStartTime = System.currentTimeMillis();
        task.name

        String moduleName = task.path.split(":")[1]
        String taskName=task.name

//        moduleTimeMap.put(moduleName, 0)
    }

    /**
     * 任务执行结束
     * @param task The task which was executed. Never null.
     * @param state The task state. If the task failed with an exception, the exception is available in this
     */
    @Override
    void afterExecute(Task task, TaskState state) {
        long cost = System.currentTimeMillis() - taskStartTime;
        System.out.println("任务:" + task.name + "," + task.path + ",耗时:" + cost + " \n")

        String timeStamp = sdf3.format(new Date())
        String moduleName = task.path.split(":")[1]
        String taskName=task.name

        Long preTotalTime = moduleTimeMap.get(moduleName)
        if (preTotalTime != null) {
            preTotalTime+=cost
            moduleTimeMap.put(moduleName, preTotalTime)
        } else {
            moduleTimeMap.put(moduleName, cost)
        }

        sb.append(timeStamp + ": 【" + moduleName + "】" + taskName + "=" + cost +  "ms \n")
        taskCounts++
    }

    private void writeToFile(String fname, String text) {
        File dir = new File(BUILD_LOG_FILE_DIR)
        if (!dir.exists()) {
            dir.mkdir()
        }

        File file = new File(BUILD_LOG_FILE_DIR + File.separator + fname + "_" + sdf2.format(new Date()) + ".log")
        file.write(text)
    }
}

gradle.addListener new BuildTimeListener()

如果查看?

当运行 app 项目,会自动在项目的根目录下创建 .build_history 文件夹,并且根据时间戳把耗时日志保存到本地,开始是每个模块的单独任务,最后进行汇总,模块的使用时间。查看对应文件即可。

.
├── app
├── build.gradle
├── buildTrace.gradle
├── .build_history
│   ├── buildTimeLog_2022_02_15_18_34_26.log
│   └── buildTimeLog_2022_02_15_18_35_50.log
├── gradle
└── testlib

给一个完整文件示例:
buildTimeLog_2022_02_15_18_35_50.log

构建时间:2022-02-15 18:35:42:193
开始构建----------------------
2022-02-15 18:35:42:280: 【app】preBuild=0ms 
2022-02-15 18:35:42:281: 【app】preDebugBuild=0ms 
2022-02-15 18:35:42:281: 【testlib】preBuild=0ms 
2022-02-15 18:35:42:282: 【testlib】preDebugBuild=0ms 
2022-02-15 18:35:42:285: 【testlib】compileDebugAidl=2ms 
2022-02-15 18:35:42:287: 【app】compileDebugAidl=1ms 
2022-02-15 18:35:42:288: 【testlib】packageDebugRenderscript=0ms 
2022-02-15 18:35:42:290: 【app】compileDebugRenderscript=1ms 
2022-02-15 18:35:42:409: 【app】dataBindingMergeDependencyArtifactsDebug=118ms 
2022-02-15 18:35:42:418: 【app】dataBindingMergeGenClassesDebug=9ms 
2022-02-15 18:35:42:419: 【app】generateDebugResValues=0ms 
2022-02-15 18:35:42:420: 【app】generateDebugResources=0ms 
2022-02-15 18:35:42:422: 【testlib】compileDebugRenderscript=0ms 
2022-02-15 18:35:42:423: 【testlib】generateDebugResValues=1ms 
2022-02-15 18:35:42:423: 【testlib】generateDebugResources=0ms 
2022-02-15 18:35:42:426: 【testlib】packageDebugResources=2ms 
2022-02-15 18:35:43:409: 【app】generateDebugBuildConfig=3ms 
2022-02-15 18:35:43:422: 【app】javaPreCompileDebug=1ms 
2022-02-15 18:35:43:423: 【testlib】writeDebugAarMetadata=0ms 
2022-02-15 18:35:43:443: 【app】createDebugCompatibleScreenManifests=1ms 
2022-02-15 18:35:43:444: 【app】extractDeepLinksDebug=1ms 
2022-02-15 18:35:43:445: 【testlib】extractDeepLinksDebug=1ms 
2022-02-15 18:35:43:449: 【testlib】processDebugManifest=2ms 
2022-02-15 18:35:43:622: 【app】processDebugMainManifest=172ms 
2022-02-15 18:35:43:627: 【app】processDebugManifest=4ms 
2022-02-15 18:35:43:631: 【app】checkDebugAarMetadata=3ms 
2022-02-15 18:35:43:634: 【testlib】compileDebugLibraryResources=1ms 
2022-02-15 18:35:43:639: 【testlib】parseDebugLocalResources=5ms 
2022-02-15 18:35:43:648: 【testlib】generateDebugBuildConfig=1ms 
2022-02-15 18:35:43:650: 【testlib】javaPreCompileDebug=0ms 
2022-02-15 18:35:43:651: 【app】mergeDebugNativeDebugMetadata=1ms 
2022-02-15 18:35:43:653: 【app】mergeDebugShaders=1ms 
2022-02-15 18:35:43:655: 【app】compileDebugShaders=1ms 
2022-02-15 18:35:43:656: 【app】generateDebugAssets=0ms 
2022-02-15 18:35:43:658: 【testlib】mergeDebugShaders=2ms 
2022-02-15 18:35:43:660: 【testlib】compileDebugShaders=1ms 
2022-02-15 18:35:43:660: 【testlib】generateDebugAssets=0ms 
2022-02-15 18:35:43:662: 【testlib】packageDebugAssets=1ms 
2022-02-15 18:35:43:683: 【app】mergeDebugAssets=20ms 
2022-02-15 18:35:43:685: 【app】compressDebugAssets=1ms 
2022-02-15 18:35:43:687: 【app】processDebugJavaRes=1ms 
2022-02-15 18:35:43:688: 【testlib】processDebugJavaRes=0ms 
2022-02-15 18:35:43:690: 【testlib】bundleLibResDebug=1ms 
2022-02-15 18:35:43:727: 【testlib】generateDebugRFile=36ms 
2022-02-15 18:35:44:855: 【testlib】compileDebugJavaWithJavac=1126ms 
2022-02-15 18:35:44:876: 【app】mergeDebugJavaResource=20ms 
2022-02-15 18:35:44:899: 【app】desugarDebugFileDependencies=1ms 
2022-02-15 18:35:44:939: 【app】mergeDebugResources=39ms 
2022-02-15 18:35:44:941: 【app】dataBindingGenBaseClassesDebug=1ms 
2022-02-15 18:35:44:943: 【app】mergeDebugJniLibFolders=1ms 
2022-02-15 18:35:44:944: 【testlib】mergeDebugJniLibFolders=0ms 
2022-02-15 18:35:44:946: 【testlib】mergeDebugNativeLibs=1ms 
2022-02-15 18:35:44:947: 【testlib】stripDebugDebugSymbols=1ms 
2022-02-15 18:35:44:948: 【testlib】copyDebugJniLibsProjectOnly=1ms 
2022-02-15 18:35:44:964: 【app】validateSigningDebug=0ms 
2022-02-15 18:35:44:965: 【app】writeDebugAppMetadata=1ms 
2022-02-15 18:35:44:966: 【app】writeDebugSigningConfigVersions=1ms 
2022-02-15 18:35:44:967: 【testlib】bundleLibRuntimeToDirDebug=2ms 
2022-02-15 18:35:44:968: 【testlib】bundleLibCompileToJarDebug=3ms 
2022-02-15 18:35:44:970: 【app】checkDebugDuplicateClasses=5ms 
2022-02-15 18:35:45:002: 【app】processDebugManifestForPackage=28ms 
2022-02-15 18:35:45:316: 【app】mergeLibDexDebug=18ms 
2022-02-15 18:35:46:448: 【app】mergeDebugNativeLibs=1149ms 
2022-02-15 18:35:46:456: 【app】stripDebugDebugSymbols=7ms 
2022-02-15 18:35:48:751: 【app】processDebugResources=2303ms 
2022-02-15 18:35:49:288: 【app】compileDebugJavaWithJavac=533ms 
2022-02-15 18:35:49:288: 【app】compileDebugSources=0ms 
2022-02-15 18:35:49:772: 【app】dexBuilderDebug=481ms 
2022-02-15 18:35:49:835: 【app】mergeExtDexDebug=62ms 
2022-02-15 18:35:50:035: 【app】mergeProjectDexDebug=262ms 
2022-02-15 18:35:50:420: 【app】packageDebug=384ms 
2022-02-15 18:35:50:421: 【app】assembleDebug=0ms 
构建结束------------------2022-02-15 18:35:50:441----
构建总耗时:8248ms 
app, 总耗时:5635ms 
testlib, 总耗时:1190ms 

这样就能看出来是 testlib 占用 1.1s, 把占比最高的,抽离成 aar 即刻实现项目编译的更快。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341

推荐阅读更多精彩内容