zoukankan      html  css  js  c++  java
  • Gradle系列之从init.gradle说起

    从maven说起

    用过maven的开发都知道,在maven里一些信息可以定义在全局的配置文件中,比如把一些仓库信息定义在用户目录/.m2/setting.xml文件中,这样就不用每个项目都配置这些相同的配置了。对于Andorid开发者来说,使用gradle比使用maven更常见,在开发中说不定你会在每一个项目中配置一遍公司的仓库地址,定义一些相同的配置,那么在gradle中有没有类似maven的这么一个配置文件可以定义一些全局信息呢?

    答案当然是有,那就是init.gradle。

    init.gradle的作用

    先来简单介绍一下init.gradle这个文件的作用。

    • 它可以用来建立公司内部的配置,如定义公司内部的仓库地址。
    • 它可以用来配置一些全局属性,比如配置持续集成服务器的地址等配置。
    • 它可以用来提供构建所需要的用户的个人信息,如仓库或数据库的用户名和密码。
    • 它可以用来定义开发者机器的环境,比如定义jdk安装在什么位置,android sdk安装在什么位置等等。
    • 最重要的功能之一,它可以用来注册一些监听器。比如监听Gradle事件的发生,做一些额外的操作,例如需要对某个项目构建前和构建后做一些操作,又例如对项目的依赖做检测,检测是否含有snapshot包,在release构建中一般来说是禁止依赖snapshot包的,所以这时候就可以扔出一个异常。
    • 重定向日志。我们可以将gradle默认的日志进行重定向,甚至我们可以不输出默认日志,自定义如何输出gradle产生的日志信息。

    init.gradle的加载顺序

    再来说说init.gradle文件的加载顺序(不一定命名为init.gradle)。Gradle会依次对一些目录进行检测,按照优先级加载这些目录下的文件,如果一个目录下有多个文件被找到,则按照英文字母的顺序依次加载。加载优先级如下:

    • 通过 -I 或者 –init-script 参数在构建开始时指定路径,如
    • gradle --init-script init.gradle clean

    gradle --I init.gradle assembleDebug

    • 加载USER_HOME/.gradle/init.gradle文件
    • 加载USER_HOME/.gradle/init.d/目录下的以.gradle结尾的文件
    • 加载GRADLE_HOME/init.d/目录下的以.gradle结尾的文件

    那么USER_HOME和GRADLE_HOME可以怎么得到呢,其实USER_HOME一般来说就是当前用户目录下的.gradle目录,而GRADLE_HOME目录一般来说是gradle的可执行目录,如果你配置了环境变量,且你使用的是环境变量中的可执行文件,则这个目录会执向你配置了环境变量的目录,比如,我配置了gradle的环境变量指向/Library/gradle,这两个目录在我电脑上的值为:

    gradleHomeDir:/Library/gradle
    gradleUserHomeDir:/Users/lizhangqu/.gradle

    但是如果我使用项目中的gradlew去执行任务,则这两个值为

    gradleHomeDir:/Users/lizhangqu/.gradle/wrapper/dists/gradle-2.10-all/a4w5fzrkeut1ox71xslb49gst/gradle-2.10
    gradleUserHomeDir:/Users/lizhangqu/.gradle

    我们可以在init.gradle中使用gradle实例来获得这两个目录,甚至可以获得gradle的版本,以及gradle启动时所携带的参数。具体脚本如下:

    //gradle的可执行目录
    gradle.println "gradleHomeDir:${gradle.gradleHomeDir}"
    //gradle的用户目录,用于缓存一些下载好的资源,编译好的构建脚本等
    gradle.println "gradleUserHomeDir:${gradle.gradleUserHomeDir}"
    //gradle的版本号
    gradle.println "gradleVersion:${gradle.gradleVersion}"
    //gralde当前构建的启动参数
    gradle.println "startParameter:${gradle.startParameter}"

    在init.gradle使用三方库

    如果我们需要在init.gradle中使用第三方库的功能,比如我需要使用apache的commons-math库中的某个函数,则可以使用initscript定义仓库地址,然后将依赖加入,如下脚本:

    initscript {
        //定义init.gradle所需要的仓库地址
        repositories {
            jcenter()
            mavenCentral()
            mavenLocal()
        }
        //加入依赖
        dependencies {
            classpath 'org.apache.commons:commons-math:2.0'
        }
    }
    //使用函数
    println org.apache.commons.math.fraction.Fraction.ONE_FIFTH.multiply(2)

    以上代码会输出2/5

    在init.gradle中定义全局的仓库

    在公司中,我所见到的仓库的定义都是定义在每一个项目中,没有一个全局的仓库配置,这样显得有点冗余,而在init.gradle中就可以定义一个公司的仓库地址,之后所有的gradle项目都可以使用这个仓库。并且对于一些实时在改变的库,我们可以定义其全局的更新策略(虽然这个策略在我的电脑上一直没有生效,但这是gradle官方标准的做法,即使用cacheChangingModulesFor属性,将其时间设为0,没有生效显然是一个bug)。

    //init.gralde可以配置一些全局的配置,比如仓库的地址等
    import java.util.concurrent.TimeUnit
    allprojects { Project project ->
        buildscript {
            repositories {
                maven {
                    url "htttp://url/to/maven"
                }
                jcenter()
                mavenCentral()
                mavenLocal()
            }
        }
        repositories {
            maven {
                url "htttp://url/to/maven"
            }
            jcenter()
            mavenCentral()
            mavenLocal()
        }
        configurations.all {
            resolutionStrategy {
                // cache dynamic versions for 10 minutes
                cacheDynamicVersionsFor 10 * 60, TimeUnit.SECONDS
                // don't cache changing modules at all
                cacheChangingModulesFor 0, TimeUnit.SECONDS
            }
        }
    }
    

    在init.gradle中自定义插件并使用

    在我们的项目中,我们会使用apply plugin来引用一些插件来增强一些功能,当然在init.gradle也可以这么做,我们就以前面定义公司的仓库和依赖的更新策略来做演示,将其修改为一个插件然后使用。如下:

    //对于一些仓库的全局定义,也可以使用插件的方式定义,如
    
    
    apply plugin: EnterpriseRepositoryPlugin
    
    class EnterpriseRepositoryPlugin implements Plugin<Gradle> {
        private static String REPOSITORY_URL = "htttp://url/to/maven"
    
        void apply(Gradle gradle) {
            gradle.allprojects { project ->
                project.buildscript {
                    repositories {
                        maven {
                            url REPOSITORY_URL
                        }
                        jcenter()
                        mavenCentral()
                        mavenLocal()
                    }
                    dependencies {
                        //可以定义全局的android gradle插件
                        classpath 'com.android.tools.build:gradle:2.1.2'
                    }
                }
                project.repositories {
                    maven {
                        url REPOSITORY_URL
                    }
                    jcenter()
                    mavenCentral()
                    mavenLocal()
                }
                project.configurations.all {
                    resolutionStrategy {
                        // cache dynamic versions for 10 minutes
                        cacheDynamicVersionsFor 10 * 60, TimeUnit.SECONDS
                        // don't cache changing modules at all
                        cacheChangingModulesFor 0, TimeUnit.SECONDS
                    }
                }
            }
        }
    }

    使用init.gradle重写日志

    默认的如果我使用gradle assembleDebug来执行构建,控制台会输出一系列的log,类似下面的内容

    ./gradlew --I init.gradle assembleDebug
    Incremental java compilation is an incubating feature.
    :app:preBuild UP-TO-DATE
    :app:preDebugBuild UP-TO-DATE
    :app:checkDebugManifest
    :app:preReleaseBuild UP-TO-DATE
    :app:prepareComAndroidSupportAnimatedVectorDrawable2411Library
    :app:prepareComAndroidSupportAppcompatV72411Library
    :app:prepareComAndroidSupportSupportV42411Library
    :app:prepareComAndroidSupportSupportVectorDrawable2411Library
    :app:prepareDebugDependencies
    :app:compileDebugAidl
    :app:compileDebugRenderscript
    :app:generateDebugBuildConfig
    :app:mergeDebugShaders
    :app:compileDebugShaders
    :app:generateDebugAssets
    :app:mergeDebugAssets
    :app:generateDebugResValues UP-TO-DATE
    :app:generateDebugResources
    :app:mergeDebugResources
    :app:processDebugManifest
    :app:processDebugResources
    :app:generateDebugSources
    :app:incrementalDebugJavaCompilationSafeguard
    :app:compileDebugJavaWithJavac
    :app:compileDebugJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
    :app:compileDebugNdk UP-TO-DATE
    :app:compileDebugSources
    :app:prePackageMarkerForDebug
    :app:transformClassesWithDexForDebug
    Merged dex #1 (5 defs/15.3KiB)
    Merged dex #2 (355 defs/574.2KiB)
    Merged dex #3 (819 defs/997.1KiB)
    Merged dex #4 (17 defs/48.5KiB)
    Merged dex #5 (29 defs/89.4KiB)
    Merged dex #6 (363 defs/280.8KiB)
    Merged dex #7 (46 defs/7.4KiB)
    Result is 1634 defs/2438.7KiB. Took 0.5s
    :app:mergeDebugJniLibFolders
    :app:transformNative_libsWithMergeJniLibsForDebug
    :app:processDebugJavaRes UP-TO-DATE
    :app:transformResourcesWithMergeJavaResForDebug
    :app:validateDebugSigning
    :app:packageDebug
    :app:zipalignDebug
    :app:assembleDebug
    
    BUILD SUCCESSFUL
    
    Total time: 29.62 secs
    

    对于这些log,我们可以使用gradle.useLogger()函数重新定义需要输出什么内容。比如我们让控制台按我们的意思进行输出,则可以定义一个类,实现BuildListener接口和TaskExecutionListener接口。再调用useLogger函数传入这个类的实例,如下:

    gradle.useLogger(new CustomEventLogger())
    
    //自定义的log输出
    class CustomEventLogger implements BuildListener, TaskExecutionListener {
        @Override
        void buildStarted(Gradle gradle) {
            println "buildStarted"
        }
    
        @Override
        void settingsEvaluated(Settings settings) {
            println "settingsEvaluated"
        }
    
        @Override
        void projectsLoaded(Gradle gradle) {
            println "projectsLoaded"
        }
    
        @Override
        void projectsEvaluated(Gradle gradle) {
            println "projectsEvaluated"
        }
    
        public void beforeExecute(Task task) {
            println "beforeExecute:[$task.name]"
        }
    
        public void afterExecute(Task task, TaskState state) {
            println "afterExecute:[$task.name]"
        }
    
        public void buildFinished(BuildResult result) {
            println 'buildFinished'
            if (result.failure != null) {
                result.failure.printStackTrace()
            }
        }
    }

    之后控制台的输出就会大变样,变成下面的内容

    ./gradlew --I init.gradle assembleDebug
    settingsEvaluated
    projectsLoaded
    Incremental java compilation is an incubating feature.
    projectsEvaluated
    beforeExecute:[preBuild]
    afterExecute:[preBuild]
    beforeExecute:[preDebugBuild]
    afterExecute:[preDebugBuild]
    beforeExecute:[checkDebugManifest]
    afterExecute:[checkDebugManifest]
    beforeExecute:[preReleaseBuild]
    afterExecute:[preReleaseBuild]
    beforeExecute:[prepareComAndroidSupportAnimatedVectorDrawable2411Library]
    afterExecute:[prepareComAndroidSupportAnimatedVectorDrawable2411Library]
    beforeExecute:[prepareComAndroidSupportAppcompatV72411Library]
    afterExecute:[prepareComAndroidSupportAppcompatV72411Library]
    beforeExecute:[prepareComAndroidSupportSupportV42411Library]
    afterExecute:[prepareComAndroidSupportSupportV42411Library]
    beforeExecute:[prepareComAndroidSupportSupportVectorDrawable2411Library]
    afterExecute:[prepareComAndroidSupportSupportVectorDrawable2411Library]
    beforeExecute:[prepareDebugDependencies]
    afterExecute:[prepareDebugDependencies]
    beforeExecute:[compileDebugAidl]
    afterExecute:[compileDebugAidl]
    beforeExecute:[compileDebugRenderscript]
    afterExecute:[compileDebugRenderscript]
    beforeExecute:[generateDebugBuildConfig]
    afterExecute:[generateDebugBuildConfig]
    beforeExecute:[mergeDebugShaders]
    afterExecute:[mergeDebugShaders]
    beforeExecute:[compileDebugShaders]
    afterExecute:[compileDebugShaders]
    beforeExecute:[generateDebugAssets]
    afterExecute:[generateDebugAssets]
    beforeExecute:[mergeDebugAssets]
    afterExecute:[mergeDebugAssets]
    beforeExecute:[generateDebugResValues]
    afterExecute:[generateDebugResValues]
    beforeExecute:[generateDebugResources]
    afterExecute:[generateDebugResources]
    beforeExecute:[mergeDebugResources]
    afterExecute:[mergeDebugResources]
    beforeExecute:[processDebugManifest]
    afterExecute:[processDebugManifest]
    beforeExecute:[processDebugResources]
    afterExecute:[processDebugResources]
    beforeExecute:[generateDebugSources]
    afterExecute:[generateDebugSources]
    beforeExecute:[incrementalDebugJavaCompilationSafeguard]
    afterExecute:[incrementalDebugJavaCompilationSafeguard]
    beforeExecute:[compileDebugJavaWithJavac]
    :app:compileDebugJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
    afterExecute:[compileDebugJavaWithJavac]
    beforeExecute:[compileDebugNdk]
    afterExecute:[compileDebugNdk]
    beforeExecute:[compileDebugSources]
    afterExecute:[compileDebugSources]
    beforeExecute:[prePackageMarkerForDebug]
    afterExecute:[prePackageMarkerForDebug]
    beforeExecute:[transformClassesWithDexForDebug]
    Merged dex #1 (5 defs/15.3KiB)
    Merged dex #2 (355 defs/574.2KiB)
    Merged dex #3 (819 defs/997.1KiB)
    Merged dex #4 (17 defs/48.5KiB)
    Merged dex #5 (29 defs/89.4KiB)
    Merged dex #6 (363 defs/280.8KiB)
    Merged dex #7 (46 defs/7.4KiB)
    Result is 1634 defs/2438.7KiB. Took 0.7s
    afterExecute:[transformClassesWithDexForDebug]
    beforeExecute:[mergeDebugJniLibFolders]
    afterExecute:[mergeDebugJniLibFolders]
    beforeExecute:[transformNative_libsWithMergeJniLibsForDebug]
    afterExecute:[transformNative_libsWithMergeJniLibsForDebug]
    beforeExecute:[processDebugJavaRes]
    afterExecute:[processDebugJavaRes]
    beforeExecute:[transformResourcesWithMergeJavaResForDebug]
    afterExecute:[transformResourcesWithMergeJavaResForDebug]
    beforeExecute:[validateDebugSigning]
    afterExecute:[validateDebugSigning]
    beforeExecute:[packageDebug]
    afterExecute:[packageDebug]
    beforeExecute:[zipalignDebug]
    afterExecute:[zipalignDebug]
    beforeExecute:[assembleDebug]
    afterExecute:[assembleDebug]
    buildFinished
    

    对于useLogger函数来讲,其传入的参数是一个Object类型,我们让其可以实现任何一个addListener方法传入的参数的接口即可,一旦调用了useLogger方法,默认的gradle事件的日志会被替换,也就是会被替换成我们自定义的日志输出,而这些接口如下:

    • org.gradle.BuildListener
    • org.gradle.api.execution.TaskExecutionGraphListener
    • org.gradle.api.ProjectEvaluationListener
    • org.gradle.api.execution.TaskExecutionListener
    • org.gradle.api.execution.TaskActionListener
    • org.gradle.api.logging.StandardOutputListener
    • org.gradle.api.tasks.testing.TestListener
    • org.gradle.api.tasks.testing.TestOutputListener
    • org.gradle.api.artifacts.DependencyResolutionListener

    使用init.gradle注册构建监听

    上面我们提到了很多接口,这些接口可以使用gradle.addBuildListener方法或者gradle.addListener方法注册到整个构建过程。

    • BuildListener

    BuildListener在整个构建过程中的特定点会回调特定的函数,如构建开始时会回调buildStarted,setting.gradle加载和评估完成后会回调settingsEvaluated,所有项目加载进来后会回调projectsLoaded,所有项目配置评估完成会回调projectsEvaluated,构建完成会回调buildFinished,具体例子如下:

    //构建监听
    gradle.addBuildListener(new BuildListener() {
        @Override
        void buildStarted(Gradle gradle) {
            //init.gradle被执行前,构建已经发生,且buildStarted已经被回调,
            // 因此后续加入的BuildListener都不会再调用buildStarted
            gradle.println("=========BuildListener:buildStarted=========")
        }
    
        @Override
        void settingsEvaluated(Settings settings) {
            //setting.gradle加载和评估配置阶段完成
            gradle.println("=========BuildListener:settingsEvaluated=========")
        }
    
        @Override
        void projectsLoaded(Gradle gradle) {
            //项目加载完成
            gradle.println("=========BuildListener:projectsLoaded=========")
        }
    
        @Override
        void projectsEvaluated(Gradle gradle) {
            //项目评估配置阶段结束
            gradle.println("=========BuildListener:projectsEvaluated=========")
        }
    
        @Override
        void buildFinished(BuildResult result) {
            //构建完成
            gradle.println("=========BuildListener:buildFinished=========")
        }
    })

    但是buildStarted这个回调有点特殊,在构建开始后,init.gradle执行前,buildStarted方法就会被回调,因此在init.gradle加入的监听器buildStarted是不会被回调的,只有gradle内部注册的才会回调。这一细节可以从gradle的源码中看到。

    import javafx.stage.Stage
    
    private BuildResult doBuild(final Stage upTo) {
        return buildOperationExecutor.run("Run build", new Factory<BuildResult>() {
            @Override
            public BuildResult create() {
                Throwable failure = null;
                try {
                    //回调buildStarted,但此时init.gradle没有被执行,时机太早
                    buildListener.buildStarted(gradle);
                    doBuildStages(upTo);
                } catch (Throwable t) {
                    failure = exceptionAnalyser.transform(t);
                }
                BuildResult buildResult = new BuildResult(upTo.name(), gradle, failure);
                buildListener.buildFinished(buildResult);
                if (failure != null) {
                    throw new ReportedException(failure);
                }
                return buildResult;
            }
        });
    }
    private void doBuildStages(Stage upTo) {
    //执行init.gradle,此时buildStarted已经被回调,之后都不会再次回调,所以init.gradle中加入的监听器buildStarted都不会被回调
    // Evaluate init scripts
        initScriptHandler.executeScripts(gradle);
    // Calculate projects
        settingsLoader.findAndLoadSettings(gradle);
    // Configure build
        buildOperationExecutor.run("Configure build", new Runnable() {
            @Override
            public void run() {
                buildConfigurer.configure(gradle);
                if (!gradle.getStartParameter().isConfigureOnDemand()) {
                    buildListener.projectsEvaluated(gradle);
                }
                modelConfigurationListener.onConfigure(gradle);
            }
        });
        if (upTo == Stage.Configure) {
            return;
        }
    // Populate task graph
        buildOperationExecutor.run("Calculate task graph", new Runnable() {
            @Override
            public void run() {
                buildConfigurationActionExecuter.select(gradle);
                if (gradle.getStartParameter().isConfigureOnDemand()) {
                    buildListener.projectsEvaluated(gradle);
                }
            }
        });
    // Execute build
        buildOperationExecutor.run("Run tasks", new Runnable() {
            @Override
            public void run() {
                buildExecuter.execute(gradle);
            }
        });
        assert upTo == Stage.Build;
    }

    ProjectEvaluationListener

    ProjectEvaluationListener主要用于监听项目的配置评估阶段,配置评估开始前和完成后会回调。如下:

    //配置评估监听
    gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
        @Override
        void beforeEvaluate(Project project) {
            //项目配置评估前回调
            gradle.println("=========ProjectEvaluationListener:beforeEvaluate ${project.getName()}=========")
        }
    
        @Override
        void afterEvaluate(Project project, ProjectState state) {
            //项目配置评估后回调
            //如果失败,则failure不为null
            gradle.println("=========ProjectEvaluationListener:afterEvaluate ${state.executed} ${state.failure}=========")
        }
    })
    

    TaskExecutionGraphListener

    TaskExecutionGraphListener这个监听器可以在项目评估完成后获得需要构建的任务的所有依赖,比如下面的代码就可以获得我所执行的task的所有依赖。

    gradle.addListener(new TaskExecutionGraphListener() {
        @Override
        void graphPopulated(TaskExecutionGraph graph) {
            gradle.println "=========from gradle.addListener graphPopulated========="
            graph.allTasks.each {
                Task task ->
                    gradle.println "=========TaskExecutionGraph:${task.getName()}========="
            }
        }
    }

    输出的内容如下:

    ./gradlew --I init.gradle assembleDebug
    =========from gradle.addListener graphPopulated=========
    =========TaskExecutionGraph:preBuild=========
    =========TaskExecutionGraph:preDebugBuild=========
    =========TaskExecutionGraph:checkDebugManifest=========
    =========TaskExecutionGraph:preReleaseBuild=========
    =========TaskExecutionGraph:prepareComAndroidSupportAnimatedVectorDrawable2411Library=========
    =========TaskExecutionGraph:prepareComAndroidSupportAppcompatV72411Library=========
    =========TaskExecutionGraph:prepareComAndroidSupportSupportV42411Library=========
    =========TaskExecutionGraph:prepareComAndroidSupportSupportVectorDrawable2411Library=========
    =========TaskExecutionGraph:prepareDebugDependencies=========
    =========TaskExecutionGraph:compileDebugAidl=========
    =========TaskExecutionGraph:compileDebugRenderscript=========
    =========TaskExecutionGraph:generateDebugBuildConfig=========
    =========TaskExecutionGraph:mergeDebugShaders=========
    =========TaskExecutionGraph:compileDebugShaders=========
    =========TaskExecutionGraph:generateDebugAssets=========
    =========TaskExecutionGraph:mergeDebugAssets=========
    =========TaskExecutionGraph:generateDebugResValues=========
    =========TaskExecutionGraph:generateDebugResources=========
    =========TaskExecutionGraph:mergeDebugResources=========
    =========TaskExecutionGraph:processDebugManifest=========
    =========TaskExecutionGraph:processDebugResources=========
    =========TaskExecutionGraph:generateDebugSources=========
    =========TaskExecutionGraph:incrementalDebugJavaCompilationSafeguard=========
    =========TaskExecutionGraph:compileDebugJavaWithJavac=========
    =========TaskExecutionGraph:compileDebugNdk=========
    =========TaskExecutionGraph:compileDebugSources=========
    =========TaskExecutionGraph:prePackageMarkerForDebug=========
    =========TaskExecutionGraph:transformClassesWithDexForDebug=========
    =========TaskExecutionGraph:mergeDebugJniLibFolders=========
    =========TaskExecutionGraph:transformNative_libsWithMergeJniLibsForDebug=========
    =========TaskExecutionGraph:processDebugJavaRes=========
    =========TaskExecutionGraph:transformResourcesWithMergeJavaResForDebug=========
    =========TaskExecutionGraph:validateDebugSigning=========
    =========TaskExecutionGraph:packageDebug=========
    =========TaskExecutionGraph:zipalignDebug=========
    =========TaskExecutionGraph:assembleDebug=========
    

    TaskExecutionListener

    TaskExecutionListener这个监听器可以用来监听构建过程中所有task构建前和构建后。每一个任务执行前会回调beforeExecute方法,执行完成后会回调afterExecute,执行的结果保存在入参TaskState中。

    //等同gradle.taskGraph.addTaskExecutionListener
    gradle.addListener(new TaskExecutionListener() {
        @Override
        void beforeExecute(Task task) {
            gradle.println "=========from gradle.addListener beforeExecute========="
            gradle.println "=========TaskExecutionListener:beforeExecute:${task.getName()}========="
        }
    
        @Override
        void afterExecute(Task task, TaskState state) {
            gradle.println "=========from gradle.addListener afterExecute========="
            gradle.println "=========TaskExecutionListener:afterExecute:${task.getName()}========="
            gradle.println "=========TaskState:[executed]${state.executed}========="
            gradle.println "=========TaskState:[didWork]${state.didWork}========="
            gradle.println "=========TaskState:[failure]${state.failure}========="
            gradle.println "=========TaskState:[skipMessage]${state.skipMessage}========="
            gradle.println "=========TaskState:[skipped]${state.skipped}========="
            gradle.println "=========TaskState:[upToDate]${state.upToDate}========="
        }
    })

    DependencyResolutionListener

    DependencyResolutionListener这个监听器可以用来监听构建过程中依赖的关系,比如可以使用这个监听器来检测release构建中是否包含snapshot包,如果包含则扔出异常停止构建。

    
    //依赖监听
    gradle.addListener(new DependencyResolutionListener() {
        @Override
        void beforeResolve(ResolvableDependencies resolvableDependencies) {
            gradle.println "DependencyResolutionListener:beforeResolve:=====${dependencies}====="
    
        }
    
        @Override
        void afterResolve(ResolvableDependencies resolvableDependencies) {
            gradle.println "DependencyResolutionListener:afterResolve:=====${dependencies}====="
    
            def projectPath = resolvableDependencies.path.toLowerCase()
    
            if (projectPath.contains("releasecompile")) {
                gradle.println "[DependencyResolutionListener] release detect:${resolvableDependencies.path}"
                resolvableDependencies.resolutionResult.allDependencies.each { dependency ->
                    if (dependency instanceof org.gradle.api.internal.artifacts.result.DefaultUnresolvedDependencyResult) {
                        gradle.println "DefaultUnresolvedDependencyResult reason: ${dependency.reason}"
                        gradle.println "DefaultUnresolvedDependencyResult failure: ${dependency.failure}"
                    } else if (dependency instanceof org.gradle.api.internal.artifacts.result.DefaultResolvedDependencyResult) {
                        String selected = dependency.selected
                        def from = dependency.from
                        gradle.println "[DependencyResolutionListener] current dependency:${selected} which is from:${from}"
                        if (selected != null && (selected.toLowerCase().contains("snapshot") || selected.toLowerCase().contains("beta"))) {
                            String errorMessage = "[DependencyResolutionListener] [Error] ${selected} from ${from} contains a snapshot or beta version. you must fix it."
                            gradle.println errorMessage
                            throw new IllegalStateException(errorMessage)
                        }
                    }
                }
            }
        }
    })

    StandardOutputListener

    StandardOutputListener这个监听器可以得到控制台输出的log,除了输出到控制台后,可以对其进行重定向,比如输出到文件中去,如下:

    //log输出的监听,可以将log输出到其他文件中去
    gradle.addListener(new StandardOutputListener() {
        static File logFile = new File("log.txt");
        static {
            if (logFile.exists()) {
                logFile.delete()
            }
            logFile.createNewFile()
        }
    
        @Override
        void onOutput(CharSequence output) {
            try {
                FileWriter writer = new FileWriter(logFile, true);
                writer.write(output.toString());
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    })

    TaskActionListener

    在一个Task中可能会有很多action,比如我们通过doFirst和doLast加入的闭包都是action,TaskActionListener监听器就可以在这些actions执行前和执行完成后得到回调,如下:

    //TaskAction监听器,Task.doFirst,Task.doLast等传入的闭包都是Action
    gradle.addListener(new TaskActionListener() {
        @Override
        void beforeActions(Task task) {
            //在所有action执行前回调
            gradle.println "**********beforeActions:${task}**********"
            task.getActions().each {
                Action action ->
                    gradle.println "**********${action}**********"
            }
        }
    
        @Override
        void afterActions(Task task) {
            //在所有action执行后回调
            gradle.println "**********afterActions:${task}**********"
            task.getActions().each {
                Action action ->
                    gradle.println "**********${action}**********"
            }
        }
    })

    TestListener和TestOutputListener

    这两个监听器主要是用于执行测试相关的task使用的,比如保存测试报告,但是实际测试中发现TestOutputListener中的方法怎么都不会回调,原因未知,如下脚本:

    //测试监听器
    gradle.addListener(new TestListener() {
        @Override
        void beforeSuite(TestDescriptor suite) {
            gradle.println "beforeSuite:=====${suite.className} ${suite.name}====="
        }
    
        @Override
        void afterSuite(TestDescriptor suite, TestResult result) {
            gradle.println "afterSuite:=====${suite.className} ${suite.name} ${result}====="
        }
    
        @Override
        void beforeTest(TestDescriptor testDescriptor) {
            gradle.println "beforeTest:=====${testDescriptor.className} ${testDescriptor.name}====="
        }
    
        @Override
        void afterTest(TestDescriptor testDescriptor, TestResult result) {
            gradle.println "afterTest:=====${testDescriptor.className} ${testDescriptor.name} ${result}====="
        }
    })
    //测试的输出监听,实际测试并没有回调
    gradle.addListener(new TestOutputListener() {
        @Override
        void onOutput(TestDescriptor testDescriptor, TestOutputEvent outputEvent) {
            gradle.println "onOutput:=====${testDescriptor} ${outputEvent}====="
            gradle.println "message:${outputEvent.message}"
            gradle.println "destination:${outputEvent.destination}"
        }
    })

    使用闭包

    除了以上监听器之外,我们还可以使用闭包来监听构建的过程中的事件回调,比如我只对构建完成这一事件感兴趣,则可以使用

    gradle.buildFinished {
        gradle.println "=========buildFinished========="
    }

    除了这一闭包,还可以使用其他几个相关的闭包对感兴趣的事件进行注册,注册后对应的事件发生便会回调该闭包。

    /**
     * 同上BuildListener和ProjectEvaluationListener
     */
    gradle.buildStarted {
        gradle.println "=========buildStarted========="
    }
    gradle.settingsEvaluated {
        gradle.println "=========settingsEvaluated========="
    }
    gradle.projectsLoaded {
        gradle.println "=========projectsLoaded========="
    }
    gradle.projectsEvaluated {
        gradle.println "=========projectsEvaluated========="
    }
    gradle.buildFinished {
        gradle.println "=========buildFinished========="
    }
    gradle.beforeProject {
        gradle.println "=========beforeProject========="
    }
    gradle.afterProject {
        gradle.println "=========afterProject========="
    }

    其他

    对于Task的依赖关系的获得,除了上面的注册TaskExecutionGraphListener监听器,还有其他的方式,即通过gradle.taskGraph来获得,具体的脚步如下:

    //当前构建的任务依赖关系图
    gradle.taskGraph.whenReady {
        TaskExecutionGraph taskGraph ->
            taskGraph.allTasks.each {
                Task task ->
                    gradle.println "=========whenReady:taskGraph:${task.getName()}========="
            }
            taskGraph.beforeTask {
                Task task ->
                    gradle.println "=========whenReady:beforeTask:${task.getName()}========="
            }
            taskGraph.afterTask {
                Task task ->
                    gradle.println "=========whenReady:afterTask:${task.getName()}========="
            }
    }
    //等同于上面的whenReady闭包中的内容
    gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
        @Override
        void graphPopulated(TaskExecutionGraph graph) {
            gradle.println "=========TaskExecutionGraphListener:graphPopulated========="
            graph.allTasks.each {
                Task task ->
                    gradle.println "=========TaskExecutionGraph:${task.getName()}========="
            }
        }
    })
    //等同于上面的whenReady闭包中的内容
    gradle.taskGraph.addTaskExecutionListener(new TaskExecutionListener() {
        @Override
        void beforeExecute(Task task) {
            gradle.println "=========TaskExecutionListener:beforeExecute:${task.getName()}========="
    
        }
    
        @Override
        void afterExecute(Task task, TaskState state) {
            gradle.println "=========TaskExecutionListener:afterExecute:${task.getName()}========="
            gradle.println "=========TaskState:[executed]${state.executed}========="
            gradle.println "=========TaskState:[didWork]${state.didWork}========="
            gradle.println "=========TaskState:[failure]${state.failure}========="
            gradle.println "=========TaskState:[skipMessage]${state.skipMessage}========="
            gradle.println "=========TaskState:[skipped]${state.skipped}========="
            gradle.println "=========TaskState:[upToDate]${state.upToDate}========="
        }
    })
    
    //获得root project
    gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
        @Override
        void graphPopulated(TaskExecutionGraph graph) {
            gradle.println "========${gradle.rootProject}========"
        }
    })

    监听器的应用(构建时间的监听)

    对于监听器,我们可以使用它来计算各个task所执行的时间,然后输出控制台,如下:

    class TimeListener implements TaskExecutionListener, BuildListener {
        private Clock clock
        private times = []
    
        @Override
        void beforeExecute(Task task) {
            clock = new org.gradle.util.Clock()
        }
    
        @Override
        void afterExecute(Task task, TaskState taskState) {
            def ms = clock.timeInMs
            times.add([ms, task.path])
            task.project.logger.warn "${task.path} spend ${ms}ms"
        }
    
        @Override
        void buildFinished(BuildResult result) {
            println "Task spend time:"
            for (time in times) {
                if (time[0] >= 50) {
                    printf "%7sms  %s
    ", time
                }
            }
        }
    
        @Override
        void buildStarted(Gradle gradle) {}
    
        @Override
        void projectsEvaluated(Gradle gradle) {}
    
        @Override
        void projectsLoaded(Gradle gradle) {}
    
        @Override
        void settingsEvaluated(Settings settings) {}
    }
    gradle.addListener(new TimeListener())

    监听器的补充 
    其实关于监听器,并非一定要在init.gradle中进行注册,我们也可以完全在我们项目中注册监听,只不过在init.gradle中可以注册一些全局的公共功能的监听器,就像上面的构建时间的计算。

    总结

    Gradle和init.gradle相关的内容大概就这么多,总结起来,其有用的东西就是各种各样的监听器,可用于构建过程中的hook,做一些自己的事情,还有就是可以进行全局的配置等。具体如何使用,可根据自己的需求进行定制。

    转载自:https://blog.csdn.net/sbsujjbcy/article/details/52079413

  • 相关阅读:
    2014年第五届蓝桥杯省赛试题(JavaA组)
    2013年第四届蓝桥杯省赛试题(JavaA组)
    2013蓝桥杯JavaA组T10 大臣的旅费(树的直径)
    CodeForces
    天梯赛 L2-006 树的遍历(序列建树)
    PAT甲 1095 解码PAT准考证/1153 Decode Registration Card of PAT(优化技巧)
    2015年第六届蓝桥杯省赛T10 生命之树(树形dp+Java模拟vector)
    ZOJ
    SPOJ
    HDU
  • 原文地址:https://www.cnblogs.com/duex/p/9205155.html
Copyright © 2011-2022 走看看