Android Gradle学习(八):统计Task执行时长

作者: 云飞扬1 | 来源:发表于2019-01-04 16:01 被阅读49次

    关于 Gradle 的基本知识,前面章节已经讲的差不多了。那么,我们现在来牛刀小试一下,看看 Gradle 有什么用武之地。

    我们在将 Android 应用程序打包成 apk 包时,有时会发现整个 build 过程特别长,短则 1、2 分钟,长则大几分钟甚至更长,特别是你要进行调试时,漫长的等待会让人很焦躁。我们在控制台可以看到整个打包过程包含很多个 task ,那么到底是哪些 task 的执行花费了大量时间内?

    Gradle 提供了很多构建生命周期钩子函数,我们可以用 TaskExecutionListener 来监听整个构建过程中 task 的执行:

    public interface TaskExecutionListener {
        
        void beforeExecute(Task task);
        
        void afterExecute(Task task, TaskState taskState);
    }
    

    在每个 task 执行前先搜集其相关信息,记录该 task 执行的开始时间等,在 task 执行完成后,记录其执行结束时间,这样就能统计出该 task 的执行时长。

    接着,我们可以用 BuildListener 来监听整个构建是否完成,在构建完成后,输出所有执行过的 task 信息,以及每个 task 的执行时长:

    public interface BuildListener {
        void buildStarted(Gradle gradle);
    
        void settingsEvaluated(Settings settings);
    
        void projectsLoaded(Gradle gradle);
    
        void projectsEvaluated(Gradle gradle);
    
        void buildFinished(BuildResult buildResult);
    }
    

    在 buildFinished 方法中,监听构建完成以及成功与否。为了方便使用,考虑做成一个 Gradle 插件,关于插件的制作,这里不赘述了,网上有很多关于 Gradle 插件制作的教程。

    不多说了,直接上代码,核心只有一个插件类:

    class BuildTimeCostPlugin implements Plugin<Project>{
    
        //用来记录 task 的执行时长等信息
        Map<String, TaskExecTimeInfo> timeCostMap = new HashMap<>()
        //用来按顺序记录执行的 task 名称
        List<String> taskPathList = new ArrayList<>()
    
        @Override
        void apply(Project project) {
            //监听每个task的执行
            project.getGradle().addListener(new TaskExecutionListener() {
                @Override
                void beforeExecute(Task task) {
                    //task开始执行之前搜集task的信息
                    TaskExecTimeInfo timeInfo = new TaskExecTimeInfo()
                    //记录开始时间
                    timeInfo.start = System.currentTimeMillis()
                    timeInfo.path = task.getPath()
                    timeCostMap.put(task.getPath(), timeInfo)
                    taskPathList.add(task.getPath())
                }
    
                @Override
                void afterExecute(Task task, TaskState taskState) {
                    //task执行完之后,记录结束时的时间
                    TaskExecTimeInfo timeInfo = timeCostMap.get(task.getPath())
                    timeInfo.end = System.currentTimeMillis()
                    //计算该 task 的执行时长
                    timeInfo.total = timeInfo.end - timeInfo.start
                }
            })
    
            //编译结束之后:
            project.getGradle().addBuildListener(new BuildListener() {
                @Override
                void buildStarted(Gradle gradle) {
    
                }
    
                @Override
                void settingsEvaluated(Settings settings) {
    
                }
    
                @Override
                void projectsLoaded(Gradle gradle) {
    
                }
    
                @Override
                void projectsEvaluated(Gradle gradle) {
    
                }
    
                @Override
                void buildFinished(BuildResult buildResult) {
                    println "---------------------------------------"
                    println "---------------------------------------"
                    println "build finished, now println all task execution time:"
                    //按 task 执行顺序打印出执行时长信息
                    for (String path : taskPathList) {
                        long t = timeCostMap.get(path).total
                        if (t >= timeCostExt.threshold) {
                            println("${path}  [${t}ms]")
                        }
                    }                println "---------------------------------------"
                    println "---------------------------------------"
                }
            })
    
        }
    
        //关于 task 的执行信息
        class TaskExecTimeInfo {
    
            long total      //task执行总时长
    
            String path
            long start      //task 执行开始时间
            long end        //task 结束时间
    
        }
    
    }
    

    接下来,我们创建一个测试工程,使用这个插件,执行 “assembleDebug” 这个构建任务,打一个 debug 的测试包出来,构建完成之后,可以在 Gradle Console 里看到本次构建里所有 task 的执行时长信息:

    ---------------------------------------
    ---------------------------------------
    build finished, now println all task execution time:
    :app:preBuild  [1ms]
    :app:preDebugBuild  [72ms]
    :app:compileDebugAidl  [8ms]
    :app:compileDebugRenderscript  [9ms]
    :app:checkDebugManifest  [2ms]
    :app:generateDebugBuildConfig  [3ms]
    :app:prepareLintJar  [2ms]
    :app:generateDebugResValues  [1ms]
    :app:generateDebugResources  [0ms]
    :app:mergeDebugResources  [60ms]
    :app:createDebugCompatibleScreenManifests  [2ms]
    :app:processDebugManifest  [9ms]
    :app:splitsDiscoveryTaskDebug  [1ms]
    :app:processDebugResources  [15ms]
    :app:generateDebugSources  [1ms]
    :app:javaPreCompileDebug  [55ms]
    :app:compileDebugJavaWithJavac  [2038ms]
    :app:compileDebugNdk  [23ms]
    :app:compileDebugSources  [1ms]
    :app:mergeDebugShaders  [21ms]
    :app:compileDebugShaders  [12ms]
    :app:generateDebugAssets  [0ms]
    :app:mergeDebugAssets  [55ms]
    :app:transformClassesWithDexBuilderForDebug  [1216ms]
    :app:transformDexArchiveWithExternalLibsDexMergerForDebug  [1871ms]
    :app:transformDexArchiveWithDexMergerForDebug  [273ms]
    :app:mergeDebugJniLibFolders  [10ms]
    :app:transformNativeLibsWithMergeJniLibsForDebug  [451ms]
    :app:transformNativeLibsWithStripDebugSymbolForDebug  [9ms]
    :app:processDebugJavaRes  [10ms]
    :app:transformResourcesWithMergeJavaResForDebug  [266ms]
    :app:validateSigningDebug  [12ms]
    :app:packageDebug  [590ms]
    :app:assembleDebug  [1ms]
    ---------------------------------------
    ---------------------------------------
    

    从上面可以看到 compileDebugJavaWithJavac 这个 task 执行时长为 2038ms,将近有2秒钟,是这里面执行时长最长的一个。由于 Gradle 支持增量构建,再次构建的时间可能就不一样了。

    上面这个插件,能不能只打印出执行时长超过 1000ms 的任务呢?结果能不能排序后输出呢?我们继续做点优化,这就需要前面介绍的 Extension 相关知识了。

    先创建一个 Extension 类,代码如下:

    class BuildTimeCostExtension {
    
        //task执行时间超过该值才会统计
        int threshold
    
        //是否按照task执行时长进行排序,true-表示从大到小进行排序,false-表示不排序
        boolean sorted
    
        void threshold(int threshold) {
            this.threshold = threshold
        }
    
        void sorted(boolean sorted) {
            this.sorted = sorted
        }
    
    }
    

    修改插件类,在插件里创建自定义 Extension:

    @Override
    void apply(Project project) {
        //创建一个 Extension,配置输出结果
        final BuildTimeCostExtension timeCostExt = project.getExtensions().create("taskExecTime", BuildTimeCostExtension)
        
        ......
        ......
    }
    

    修改 task 执行时长输出结果的代码,根据配置来输出不同的结果:

    @Override
    void buildFinished(BuildResult buildResult) {
        println "---------------------------------------"
        println "---------------------------------------"
        println "build finished, now println all task execution time:"
        if (timeCostExt.sorted) {
            //进行排序
            List<TaskExecTimeInfo> list = new ArrayList<>()
            for (Map.Entry<String, TaskExecTimeInfo> entry : timeCostMap) {
                list.add(entry.value)
            }
            Collections.sort(list, new Comparator<TaskExecTimeInfo>() {
                @Override
                int compare(TaskExecTimeInfo t1, TaskExecTimeInfo t2) {
                    return t2.total - t1.total
                }
            })
            for (TaskExecTimeInfo timeInfo : list) {
                long t = timeInfo.total
                if (t >= timeCostExt.threshold) {
                    println("${timeInfo.path}  [${t}ms]")
                }
            }
        } else {
            //按 task 执行顺序打印出执行时长信息
            for (String path : taskPathList) {
                long t = timeCostMap.get(path).total
                if (t >= timeCostExt.threshold) {
                    println("${path}  [${t}ms]")
                }
            }
        }
        println "---------------------------------------"
        println "---------------------------------------"
    }
    

    在 build.gradle 里增加配置:

    taskExecTime {
        threshold 100
        sorted true
    }
    

    再来看看输出结果:

    ---------------------------------------
    ---------------------------------------
    build finished, now println all task execution time:
    :app:mergeDebugResources  [1109ms]
    :app:transformDexArchiveWithExternalLibsDexMergerForDebug  [644ms]
    :app:processDebugResources  [503ms]
    :app:transformClassesWithDexBuilderForDebug  [464ms]
    :app:packageDebug  [341ms]
    :app:compileDebugJavaWithJavac  [329ms]
    :app:transformDexArchiveWithDexMergerForDebug  [170ms]
    :app:transformResourcesWithMergeJavaResForDebug  [122ms]
    :app:transformNativeLibsWithMergeJniLibsForDebug  [122ms]
    ---------------------------------------
    ---------------------------------------
    
    系列文章

    Android Gradle学习(一):Gradle基础入门
    Android Gradle学习(二):如何创建Task
    Android Gradle学习(三):Task进阶学习
    Android Gradle学习(四):Project详解
    Android Gradle学习(五):Extension详解
    Android Gradle学习(六):NamedDomainObjectContainer详解
    Android Gradle学习(七):Gradle构建生命周期
    Android Gradle学习(八):统计Task执行时长

    相关文章

      网友评论

        本文标题:Android Gradle学习(八):统计Task执行时长

        本文链接:https://www.haomeiwen.com/subject/qivqrqtx.html