Skip to content

Gradle

更新: 1/24/2026 字数: 0 字 时长: 0 分钟

Gradle中的DSL

Groovy DSL

新建项目后看一下app/build.gradle:

groovy
plugins {
    alias(libs.plugins.android.application)
}

android {
    namespace 'com.generals.gradlestudy'
    compileSdk 36

    defaultConfig {
        applicationId "com.generals.gradlestudy"
        minSdk 26
        targetSdk 36
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }
}

dependencies {

    implementation libs.appcompat
    implementation libs.material
    implementation libs.activity
    implementation libs.constraintlayout
    testImplementation libs.junit
    androidTestImplementation libs.ext.junit
    androidTestImplementation libs.espresso.core
}

第一段中的代码用于声明引入Gradle插件plugin的DSL代码。完整版:

groovy
plugins {
    id 'com.android.application' version '7.3.0' apply false
}

而我们发现现在创建的默认写的是alias(libs.plugins.android.application),这是因为官方推荐用Version Catalog的方式进行集中管理,gradle会自动进行展开引用。

调用的是PluginDependenciesSpec中的id(String id)函数,返回PluginDependencySpec对象,PluginDependencySpec对象可以理解为是PluginDependenciesSpec的一层封装,比如id(String id)函数只有一个参数,那versionapply哪里来的呢,就是在PluginDependencySpec对象里的。

plugins这段代码实际上是一个函数,plugins{}里面接收的是一个闭包,也可以写成下面这样:

groovy
plugins ({
    id 'com.android.application'
})

在kotlin的写法中,则是:

kotlin
plugins {
    id("com.android.application")
}

// 完整版
plugins {
    id("com.android.application") version "7.3.0" apply false
}

简单介绍一下闭包:

groovy
def myColure = { param ->
    param + 1
}
println(myColure(1))

就可以理解成一个函数,可以接收参数,可以发现有点类似于kotlin中的高阶函数

如何测试gradle/groovy:可以使用task来进行测试,例如:

groovy
tasks.register("testGroovy") {
 doLast {
     def list = [1, 2, 3]
     list.each { println it }

     def map = [a: 1, b: 2]
     println map.a
 }
}

然后点击左边的绿色三角形运行即可,或者命令行输入./gradlew testGroovy

另,也可以在Tools-Groovy Console里进行测试。

groovy的语法在这篇文章上有:https://juejin.cn/post/7166638852503765006

Gradle的生命周期

三个阶段

Gradle在评估和运行构建时有三个阶段:

  1. Initialization(初始化)。Gradle会决定构建中包含哪些项目,并为每个项目创建Project实例。为了决定构建中会包含哪些项目,Gradle首先会寻找settings.gradle来决定此次为单项目构建还是多项目构建,单项目就是module,多项目即project+app+module(1+n)。
  2. Configuration(配置)阶段,Gradle会评估构建项目中包含的所有构建脚本,随后应用插件、使用DSL配置构建,并在最后注册Task,同时惰性注册它们的输入,因为并不一定会执行。
  3. Execution(执行)阶段,Gradle会执行构建所需的Task集合。

生命周期本质就是在各个阶段把Task组合起来,然后去构建项目。Task是Gradle构建的核心,Task之间是有依赖的,Gradle会在构建期间来生成依赖关系图,即Task集合。

Initialization

Settings

build.gradle里面的配置和方法调用委托的是Project对象,而同样是构建脚本的settings.gradle里面的配置和方法调用委托的是Settings对象。

在Gradle构建时会创建一个Settings实例,并根据它执行设置文件。Settings实例和settings.gradle文件是一对一的对应关系。

项目管理

对于settings.gradle文件:

  • 单项目构建,这个文件是可选的。
  • 多项目构建,这个文件是必须的,且必须位于项目的根目录下。

多项目构建的settings.gradle文件(这里以kts为例):

kotlin
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "WanAndroid_Multi"
include(":app")
include(":lib_util")
include(":lib_base")
include(":module_main")
include(":lib_config")
include(":module_login")
include(":module_login:api_login")

关键是include,表示给指定的项目添加到构建中,可以指向我们项目包含的module路径,也可以指向硬盘中子项目的绝对路径。

插件管理

settings.gradle除了管理项目外,还能管理插件(Plugin),即pluginManagementpluginManagement中指定了插件所需要的下载仓库地址。

pluginManagement配置是由PluginManagementSpec接口类解析的,有五个方法:

  1. includeBuild(rootProject)
  2. includeBuild(rootProject, configuration)
  3. plugins(action)
  4. repositories(repositoriesAction)
  5. resolutionStrategy(action)

我们主要用到的是resolutionStrategy:

java
@HasInternalProtocol
public interface PluginResolutionStrategy {

    /**
     * Adds an action that is executed for each plugin that is resolved.
     * The {@link PluginResolveDetails} parameter contains information about
     * the plugin that was requested and allows the rule to modify which plugin
     * will actually be resolved.
     */
    void eachPlugin(Action<? super PluginResolveDetails> rule);

}

PluginResolutionStrategy允许在PluginRequest之前对其进行修改,并有唯一回调eachPlugin,eachPlugin的参数类型是PluginResolveDetails。

PluginResolveDetails:

java
public interface PluginResolveDetails {

    /**
     * 获取请求的插件,返回PluginRequest对象,包含id,version,module信息
     */
    PluginRequest getRequested();

    /**
     * 设置插件的模块
     *
     * @param notation the module to use, supports the same notations as {@link org.gradle.api.artifacts.dsl.DependencyHandler}
     */
    void useModule(Object notation);

    /**
     * 设置插件的版本
     *
     * @param version version to use
     */
    void useVersion(@Nullable String version);

    /**
     * 请求的目标插件
     */
    PluginRequest getTarget();

}

插件替换主要用到的就是useModule方法:

kotlin
     resolutionStrategy {
         eachPlugin {
            if (requested.id.id == "org.gradle.sample") {
                useModule("xxx")
            }
         }
     }

useVersion("2.0")则可以设置插件版本。

设置插件版本后,在所有的build script中通过plugins{}引入插件则无需再次指定版本。

Configuration

在Configuration(配置)阶段,Gradle会评估构建项目中包含的所有构建脚本,然后应用插件,使用DSL配置构建,并在最后注册Task,同时惰性注册它们的输入,因为并不一定会执行。

配置阶段无论请求哪个Task都会执行,所以要避免在配置阶段执行任何耗时操作。

配置阶段就是创建Project对象,执行build.gradle文件,并创建对应的Task依赖关系图。

Project

Gradle构建过程中,会根据Settings对象解析出来的项目结构为每个项目创建一个Project对象。在生成Task依赖关系图之前,Project对象还做了几件事:引入插件、配置属性以及编译依赖。

引入插件
kotlin
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
}

plugins是Project对象的一个方法,用于设置当前模块所使用的插件。

配置属性
kotlin
android {
    namespace = "com.generals.wanandroid_multi"
    compileSdk = 35

    defaultConfig {
        applicationId = "com.generals.wanandroid_multi"
        minSdk = 26
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = "11"
    }
}

android{}配置实际是application插件的DSL配置,这些配置都是通过DSL对插件进行配置,这些配置会影响插件的执行,从而影响整个构建流程。

编译依赖

就是dependencies里面的内容,除了官方库以外,还可以添加常用的三方库。

Execution

在 Execution (执行) 阶段,Gradle会执行构建所需的Task集合。

其实这个阶段才是真正的编译打包,于Android而言,比如我们常见的compileDebugJavaWithJavac、mergeDebugNativeLibs等等。

Gradle常用命令与参数

Gradle命令

Gradle执行命令主要用到的是Gradle Wrapper,我们常用的./gradlew,gradlew即Gradle Wrapper的简写。

看下gradlew的脚本内容:

bash
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=`expr $i + 1`
    done
    case $i in
        0) set -- ;;
        1) set -- "$args0" ;;
        2) set -- "$args0" "$args1" ;;
        3) set -- "$args0" "$args1" "$args2" ;;
        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

exec "$JAVACMD" "$@"
  1. 获取电脑系统内核的信息,JRE环境信息等;
  2. 设置classpath路径;
  3. 执行java命令工具,调用gradle jar包的class文件;

查看命令

使用./gradlew --help查看支持哪些命令:

-?, -h, --help                     Shows this help message.
-a, --no-rebuild                   Do not rebuild project dependencies.
-b, --build-file                   Specify the build file. [deprecated]
--build-cache                      Enables the Gradle build cache. Gradle will try to reuse outputs from previous builds.
--no-build-cache                   Disables the Gradle build cache.
-c, --settings-file                Specify the settings file. [deprecated]
--configuration-cache              Enables the configuration cache. Gradle will try to reuse the build configuration from previous builds.
--no-configuration-cache           Disables the configuration cache.
--configuration-cache-problems     Configures how the configuration cache handles problems (fail or warn). Defaults to fail.
--configure-on-demand              Configure necessary projects only. Gradle will attempt to reduce configuration time for large multi-project builds. [incubating]
--no-configure-on-demand           Disables the use of configuration on demand. [incubating]
--console                          Specifies which type of console output to generate. Values are 'plain', 'auto' (default), 'rich' or 'verbose'.
--continue                         Continue task execution after a task failure.
--no-continue                      Stop task execution after a task failure.
-D, --system-prop                  Set system property of the JVM (e.g. -Dmyprop=myvalue).
-d, --debug                        Log in debug mode (includes normal stacktrace).
--daemon                           Uses the Gradle daemon to run the build. Starts the daemon if not running.
--no-daemon                        Do not use the Gradle daemon to run the build. Useful occasionally if you have configured Gradle to always run with the daemon by default.    
--export-keys                      Exports the public keys used for dependency verification.
-F, --dependency-verification      Configures the dependency verification mode. Values are 'strict', 'lenient' or 'off'.
--foreground                       Starts the Gradle daemon in the foreground.
-g, --gradle-user-home             Specifies the Gradle user home directory. Defaults to ~/.gradle
-I, --init-script                  Specify an initialization script.
-i, --info                         Set log level to info.
--include-build                    Include the specified build in the composite.
-M, --write-verification-metadata  Generates checksums for dependencies used in the project (comma-separated list)
-m, --dry-run                      Run the builds with all task actions disabled.
--max-workers                      Configure the number of concurrent workers Gradle is allowed to use.
--offline                          Execute the build without accessing network resources.
-P, --project-prop                 Set project property for the build script (e.g. -Pmyprop=myvalue).
-p, --project-dir                  Specifies the start directory for Gradle. Defaults to current directory.
--parallel                         Build projects in parallel. Gradle will attempt to determine the optimal number of executor threads to use.
--no-parallel                      Disables parallel execution to build projects.
--priority                         Specifies the scheduling priority for the Gradle daemon and all processes launched by it. Values are 'normal' (default) or 'low'
--profile                          Profile build execution time and generates a report in the <build_dir>/reports/profile directory.
--project-cache-dir                Specify the project-specific cache directory. Defaults to .gradle in the root project directory.
-q, --quiet                        Log errors only.
--refresh-keys                     Refresh the public keys used for dependency verification.
--rerun-tasks                      Ignore previously cached task results.
-S, --full-stacktrace              Print out the full (very verbose) stacktrace for all exceptions.
-s, --stacktrace                   Print out the stacktrace for all exceptions.
--scan                             Creates a build scan. Gradle will emit a warning if the build scan plugin has not been applied. (https://gradle.com/build-scans)
--no-scan                          Disables the creation of a build scan. For more information about build scans, please visit https://gradle.com/build-scans.
--status                           Shows status of running and recently stopped Gradle daemon(s).
--stop                             Stops the Gradle daemon if it is running.
-t, --continuous                   Enables continuous build. Gradle does not exit and will re-execute tasks when task file inputs change.
-U, --refresh-dependencies         Refresh the state of dependencies.
--update-locks                     Perform a partial update of the dependency lock, letting passed in module notations change version. [incubating]
-V, --show-version                 Print version info and continue.
-v, --version                      Print version info and exit.
-w, --warn                         Set log level to warn.
--warning-mode                     Specifies which mode of warnings to generate. Values are 'all', 'fail', 'summary'(default) or 'none'
--watch-fs                         Enables watching the file system for changes, allowing data about the file system to be re-used for the next build.
--no-watch-fs                      Disables watching the file system.
--write-locks                      Persists dependency resolution for locked configurations, ignoring existing locking information if it exists
-x, --exclude-task                 Specify a task to be excluded from execution.
--                                 Signals the end of built-in options. Gradle parses subsequent parameters as only tasks or task options.

命令结构为gradle [taskName...] [--option-name...],多个任务用空格分隔。

下面将一些常用命令分类并列举。

Gradle相关

查看Gradle版本

可以在gradle->wrapper->gradle-wrapper.properties文件下查看distributionUrl所使用的gradle版本下载地址:

distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip

也可以在命令行中使用.gradlew -version或者./gradlew -v:

bash
------------------------------------------------------------
Gradle 8.9
------------------------------------------------------------

Build time:    2024-07-11 14:37:41 UTC
Revision:      d536ef36a19186ccc596d8817123e5445f30fef8

Kotlin:        1.9.23
Groovy:        3.0.21
Ant:           Apache Ant(TM) version 1.10.13 compiled on January 4 2023
Launcher JVM:  23 (Oracle Corporation 23+37-2369)
Daemon JVM:    C:\Program Files\Java\jdk-23 (no JDK specified, using current Java home)
OS:            Windows 11 10.0 amd64
升级Gradle

常见的升级Gradle有3种方式。

第一种,先手动修改wrapper.properties文件下distributionUrl指向的版本,再手动修改Android Gradle Plugin(AGP)版本,然后重新sync。

第二种,打开file>Project Structure修改AGP和Gradle的版本,然后apply。

第三种,使用命令行(官方推荐):

bash
./gradlew wrapper --gradle-version 7.5.1
编译命令

检查依赖并编译打包:

bash
./gradlew build

编译并打出Debug包:

bash
./gradlew assembleDebug

编译打出Debug包并安装:

bash
./gradlew installDebug

编译并打出Release包:

bash
./gradlew assembleRelease

编译打出Release包并安装:

bash
./gradlew installRelease

Debug/Release编译并打印日志:

bash
./gradlew assembleDebug --info
// or
./gradlew assembleRelease --info

清除命令:

bash
./gradlew clean

清除构建目录下的产物,等同于Build->Clean Project。

卸载命令

卸载debug/release包:

bash
./gradlew uninstallDebug
// or
./gradlew uninstallRelease

也可以通过adb执行,只不过需要执行包名:

bash
adb uninstall com.generals.wanandroid_multi
调试命令

当我们遇到编译错误的时候,经常会看到这个提示:

bash
* Try:
> Run gradle tasks to get a list of available tasks.
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

编译并打印堆栈日志:

bash
./gradlew assembleDebug --stacktrace
// or
./gradlew assembleDebug -s

有时候构建日志会有很多,看到的可能不全,甚至不是真正的编译问题,而构建日志又不能像logcat那样可以可视化的筛选,这个时候就需要用日志级别来筛选一下。

bash
-q,--quiet
仅记录错误。

-w,--warn
将日志级别设置为警告。

-i,--info
将日志级别设置为信息。

-d,--debug
调试模式(包括正常的stacktrace)。
任务相关

查看主要Task:

bash
./gradlew tasks

查看所有Task:

bash
./gradlew tasks --all

执行Task:

bash
./gradlew taskName
// or
./gradlew :moduleName:taskName

当然,通常用的是AS自带的Gradle工具,能够查看项目以及module的Task,点击可以执行。

查看依赖

查看项目根目录下的依赖:

bash
./gradlew dependencies

查看app模块下的依赖:

bash
./gradlew app:dependencies

查看依赖输出到文件:

bash
./gradlew app:dependencies > dependencies.txt
性能相关

离线编译:

bash
./gradlew assembleDebug --offline

构建缓存:

bash
./gradlew assembleDebug --build-cache // 开启

./gradlew assembleDebug --no-build-cache // 不开启

配置缓存:

bash
./gradlew assembleDebug --configuration-cache // 开启

./gradlew assembleDebug --no-configuration-cache // 不开启

并行构建:

bash
./gradlew assembleDebug --parallel // 开启

./gradlew assembleDebug --no-parallel // 不开启

上面几个配置都可以在gradle.properties中配置。

编译并输出性能报告:

bash
./gradlew assembleDebug --profile

性能报告位于构建项目的/build/reports/profile/路径下。

动态传参

再来介绍一个比较常用的传参属性,--project-prop,我们一般常用-P表示,用来设置根项目的项目属性。

获取参数:

bash
 ./gradlew assembleDebug -PisTest=true

这里我们用-P传入了一个isTest字段,并赋值为true。

然后我们在build.gradle里编写如下代码:

kotlin
if (hasProperty("isTest")){
    println("---hasProperty isTest yes")
}else {
    println("---hasProperty isTest no")
}

这样就能获取参数,module或插件也可以这么获取:

kotlin
project.property('isTest')

getProperty()也可以获取参数值:

kotlin
if (project.hasProperty("isTest")) {
    println("---hasProperty isTest yes")

    val isTest = project.property("isTest").toString().toBoolean()
    if (isTest) {
        println("---isTest true")
    } else {
        println("---isTest false")
    }
} else {
    println("---hasProperty isTest no")
}

在kotlin中,也可以写成下列形式:

kotlin
val isTest = project.findProperty("isTest")?.toString()?.toBoolean()

if (isTest != null) {
 println("---hasProperty isTest yes")
 if (isTest) {
     println("---isTest true")
 } else {
     println("---isTest false")
 }
} else {
 println("---hasProperty isTest no")
}

构建核心——Task

Task是什么

前面提到过,Task就是一个任务,gradle中的最小的构建单元,而gradle构建的核心就是由Task组成的有向无环图:

Task主要是管理了一个Action的List,既可以在List前面插入Action(doFirst),也可以在list后面插入Action(doLast),Action是最小的执行单元。

创建Task

我们可以直接在build.gradle文件中创建Task,因为一个build.gradle文件对应一个Project对象,而Task的来源需要先确定哪些项目参与编译,这里的项目我们即认为是这个Project对象。故我们可以直接在build.gradle文件中创建Task。

创建Task需要使用TaskContainer的register方法,有下列这几种方式:

  1. register(String name, Action<? super Task> configurationAction)

  2. register(String name, Class type, Action<? super T> configurationAction)

  3. register(String name, Class type)

  4. register(String name, Class type, Object... constructorArgs)

  5. register(String name)

常用的是1,2。其中configurationAction指的是Action,即Task的操作,type类型指的是Task类型,可以是自定义类型,也可以是指定自带的Copy,Delete,Zip,Jar等类型。比如我们这里创建一个Task:

kotlin
tasks.register("testTask") {
    println("Task name: ${this.name}")
}

在Project中也提供了直接创建Task的方法:

kotlin
task("testTask") {
    println("Task name: ${this.name}")
}

执行Task

执行单个Task

终端输入命令:

cmd
./gradlew taskname

当然,也可以点击任务左边的绿色三角形运行。运行结果如下:

执行多个Task

cmd
./gradlew taskname taskname taskname

Task之间不能同名,如果同名则会编译失败。

Task的执行结果

编译时Task后面会有一个标签,表示Task的执行结果:

> Task :app:createDebugVariantModel UP-TO-DATE
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:mergeDebugNativeDebugMetadata NO-SOURCE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
  • EXCUTED 表示Task执行
  • UP-TO-DATE 表示Task输出没有改变
  • FOME-CACHE 表示可以从缓存中复用上一次的执行结果
  • SKIPPED 表示跳过
  • NO-SOURCE 表示Task不需要执行

Task的Action

Action就是编译时所需的操作,可以有多个,多个Task一半是我们在自定义的时候使用。

自定义Task

kotlin
abstract class CustomTask : DefaultTask() {
    @Internal
    var taskName = "custom"

    @TaskAction
    fun customAction1() {
        println("$taskName -- Action1")
    }

    @TaskAction
    fun customAction2() {
        println("$taskName -- Action2")
    }

}

tasks.register("customTask", CustomTask::class.java) {
    taskName = "myCustomTask"
}
  • 自定义一个抽象类,继承自DefaultTask
  • Action方法要添加@TaskAction注解
  • 对外暴露的参数需要使用@Internal注解

使用时传入自定义的Task的类就好了:

> Task :app:customTask
myCustomTask -- Action2
myCustomTask -- Action1
  • doFirst:属于Action的一种,在Task Action的头部执行。可以有多个。
  • doLast:属于Action的一种,在Task Action的尾部执行。可以有多个。
kotlin
tasks.register("taskk") {
    doFirst {
        println("$name = doFirst 111")
    }
    doFirst {
        println("$name = doFirst 222")
    }

    println("Task Name = $name")

    doLast {
        println("$name = doLast 111")
    }
    doLast {
        println("$name = doLast 222")
    }
}

上述代码的执行结果:

Task Name = taskk

> Task :app:taskk
taskk = doFirst 222
taskk = doFirst 111
taskk = doLast 111
taskk = doLast 222

Task Name的输出是在Gradle生命周期的配置阶段,因为它就在闭包下面,不在任何Action里,没有执行时机,配置阶段解析到这个Task就会执行println。

其他输出都是在Task :app:yechaoa下,因为有明确的Action执行时机。

Action执行顺序

doFirst(倒叙)->Action(倒叙)->doLast(正序)

Task属性

Task有下面几个属性:

kotlin
    String TASK_NAME = "name";

    String TASK_DESCRIPTION = "description";

    String TASK_GROUP = "group";

    String TASK_TYPE = "type";

    String TASK_DEPENDS_ON = "dependsOn";

    String TASK_OVERWRITE = "overwrite";

    String TASK_ACTION = "action";

配置了属性和分组后,可以在AS右侧可视化面板看到这个任务,双击就可以执行。

Task依赖

dependsOn

kotlin
tasks.register("task111") {
//    dependsOn(tasks.named("task222"))
    dependsOn(tasks.matching { it.name.contains("task222") })
    doLast {
        println(name)
    }
}

tasks.register("task222") {
    doLast {
        println(name)
    }
}

输出结果:

> Task :app:task222
task222

> Task :app:task111
task111> Task :app:task222
task222

> Task :app:task111
task111

常用的是下列这种写法:

kotlin
val task111 = tasks.register("task111") {
    doLast {
        println(name)
    }
}

val task222 = tasks.register("task222") {
    doLast {
        println(name)
    }
}

task111.dependsOn(task222)

这里dependsOn()也即可以是名称也可以是路径,也可以是一个type类型,其他项目的project也可以:

kotlin
task111.dependsOn("project-lib:xxx")

finalizedBy

为Task添加指定的终结器任务。也就是指定下一个执行的Task,dependsOn指定的是上一个。

mustRunAfter

kotlin
task111.configure {
    mustRunAfter(task222)
}

这时候执行task111时会发现222并没有执行,我们要查看依赖关系需要一起执行:

cmd
./gradlew task111 task222

然后就能看到结果了。

shouldRunAfter

与mustAfter写法一致,规则也类似,但不太一样,因为它在两种情况下会被忽略。首先,如果使用该规则会引入一个排序周期;其次,当使用并行执行时,除了“应该运行”任务外,任务的所有依赖项都已满足,那么无论其“应该运行”依赖项是否已运行,都将运行此任务。

跳过Task

条件跳过

Gradle提供了onlyIf(Closure onlyIfClosure)方法,只有闭包的结果返回True时,才执行Task。

kotlin
tasks.register("skipTask") {
    onlyIf {
        providers.gradleProperty("aaa").isPresent
    }
    doLast {
        println("$name is Executed")
    }
}

执行:

cmd
./gradlew skipTask -Paaa

输出:

> Task :app:skipTask
skipTask is Executed

这里的条件是判断是否有aaa这个参数,即加上-Paaa后才会执行这个Task。

异常跳过

如果onlyIf不满足需求,也可以使用StopExecutionException来跳过。

StopExecutionException属于异常,当抛出异常的时候,会跳过当前Action及后续Action,即跳过当前Task执行下一个Task。

禁用跳过

每个Task都有一个enabled开关,false表示禁用,禁用后任何操作都不会被执行。

超时跳过

kotlin
timeout = Duration.ofSeconds(10)

如果Task的运行时间超过指定的时间,则执行该任务的线程将被中断。