Bỏ qua để đến nội dung

Custom Tasks

Gradle Tasks là đơn vị công việc cơ bản trong Gradle. Ngoài các tasks được cung cấp bởi plugins (như assembleDebug), bạn có thể tạo custom tasks để tự động hóa các công việc lặp đi lặp lại.

Mỗi task trong Gradle thực hiện một công việc cụ thể và có thể phụ thuộc vào các tasks khác.

Terminal window
# Tất cả tasks
./gradlew tasks
# Tasks với mô tả
./gradlew tasks --all
# Tasks của module cụ thể
./gradlew :app:tasks
Task Mô tả
assembleDebug Build APK debug
assembleRelease Build APK release
bundleRelease Build AAB release
clean Xóa build outputs
test Chạy unit tests
connectedAndroidTest Chạy instrumented tests
lint Chạy lint checks

// build.gradle.kts (module)
tasks.register("hello") {
group = "custom"
description = "A simple hello task"
doLast {
println("Hello from Gradle!")
}
}

Chạy task:

Terminal window
./gradlew hello
tasks.register("greeting") {
doFirst {
println("Preparing to greet...")
}
doLast {
println("Hello, World!")
}
doLast {
println("Goodbye!")
}
}

📝 Lưu ý: doFirst chạy trước, doLast chạy sau. Có thể có nhiều doLast blocks và chúng chạy theo thứ tự khai báo.


flowchart LR
subgraph "dependsOn"
B1["buildAll"] --> A1["assembleDebug"]
B1 --> A2["assembleRelease"]
end
subgraph "finalizedBy"
D1["deploy"] --> N1["notify"]
N1 -.->|"always runs after"| D1
end
subgraph "mustRunAfter"
T1["taskA"] --> T2["taskB"]
end
style B1 fill:#e3f2fd
style D1 fill:#fff3e0
style T1 fill:#e8f5e9

Task phụ thuộc vào task khác:

tasks.register("buildAll") {
group = "build"
description = "Build all variants"
dependsOn("assembleDebug", "assembleRelease")
}

Task chạy sau khi task khác hoàn thành:

tasks.register("deploy") {
doLast {
println("Deploying...")
}
}
tasks.register("notify") {
doLast {
println("Deployment complete! Notifying team...")
}
}
tasks.named("deploy") {
finalizedBy("notify")
}

Xác định thứ tự khi cả hai tasks được yêu cầu:

tasks.register("taskA") {
doLast { println("A") }
}
tasks.register("taskB") {
mustRunAfter("taskA")
doLast { println("B") }
}
// ./gradlew taskB taskA → prints "A" then "B"

Gradle sử dụng inputs/outputs để xác định task có cần chạy lại hay không (incremental builds).

tasks.register<Copy>("copyDocs") {
group = "documentation"
description = "Copy documentation to output"
from("docs/")
into(layout.buildDirectory.dir("documentation"))
include("*.md", "*.txt")
exclude("**/draft/**")
}
abstract class GenerateVersionFile : DefaultTask() {
@get:Input
abstract val versionName: Property<String>
@get:Input
abstract val versionCode: Property<Int>
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun generate() {
val content = """
version.name=${versionName.get()}
version.code=${versionCode.get()}
build.time=${System.currentTimeMillis()}
""".trimIndent()
outputFile.get().asFile.writeText(content)
}
}
tasks.register<GenerateVersionFile>("generateVersionInfo") {
group = "build"
versionName.set("1.0.0")
versionCode.set(1)
outputFile.set(layout.buildDirectory.file("version.properties"))
}

tasks.register("cleanBuild") {
group = "build"
description = "Clean and build release APK"
dependsOn("clean", "assembleRelease")
tasks.findByName("assembleRelease")?.mustRunAfter("clean")
}
tasks.register("printBuildInfo") {
group = "help"
description = "Print build information"
doLast {
println("=== Build Info ===")
println("Gradle version: ${gradle.gradleVersion}")
println("JVM version: ${System.getProperty("java.version")}")
println("OS: ${System.getProperty("os.name")}")
println("Project: ${project.name}")
println("Build directory: ${layout.buildDirectory.get()}")
}
}
tasks.register("checkOutdatedDeps") {
group = "verification"
description = "Check for outdated dependencies"
doLast {
exec {
commandLine("./gradlew", "dependencyUpdates", "-Drevision=release")
}
}
}
tasks.register("generateBuildReport") {
group = "reporting"
description = "Generate a build report"
val reportDir = layout.buildDirectory.dir("reports/custom")
outputs.dir(reportDir)
doLast {
val reportFile = reportDir.get().file("build-report.md").asFile
reportFile.parentFile.mkdirs()
reportFile.writeText("""
# Build Report
- Date: ${java.time.LocalDateTime.now()}
- Project: ${project.name}
- Gradle: ${gradle.gradleVersion}
## Modules
${rootProject.subprojects.joinToString("\n") { "- ${it.name}" }}
""".trimIndent())
println("Report generated: ${reportFile.absolutePath}")
}
}
tasks.register("checkApkSize") {
group = "verification"
description = "Check APK size"
dependsOn("assembleRelease")
doLast {
val apkDir = layout.buildDirectory.dir("outputs/apk/release").get()
val apkFiles = apkDir.asFile.listFiles { file ->
file.name.endsWith(".apk")
}
apkFiles?.forEach { apk ->
val sizeMB = apk.length() / (1024.0 * 1024.0)
println("${apk.name}: %.2f MB".format(sizeMB))
if (sizeMB > 100) {
logger.warn("⚠️ APK size exceeds 100MB!")
}
}
}
}

Khi có nhiều custom tasks, nên tổ chức chúng trong một plugin:

  1. Tạo thư mục buildSrc/:
buildSrc/
├── build.gradle.kts
└── src/main/kotlin/
└── CustomTasksPlugin.kt
  1. buildSrc/build.gradle.kts:
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
  1. CustomTasksPlugin.kt:
import org.gradle.api.Plugin
import org.gradle.api.Project
class CustomTasksPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.tasks.register("hello") {
group = "custom"
doLast {
println("Hello from plugin!")
}
}
project.tasks.register("printModules") {
group = "custom"
doLast {
project.rootProject.subprojects.forEach {
println("Module: ${it.name}")
}
}
}
}
}
  1. Apply trong build.gradle.kts:
apply<CustomTasksPlugin>()

tasks.register("myTask") {
group = "custom" // Giúp phân loại trong ./gradlew tasks
description = "What this task does" // Mô tả rõ ràng
}
tasks.register("processData") {
inputs.file("data/input.json")
outputs.file(layout.buildDirectory.file("output.json"))
doLast {
// Process data
}
}
// ❌ Chạy trong configuration phase
tasks.register("bad") {
val result = heavyComputation() // Chạy mỗi lần sync
doLast { println(result) }
}
// ✅ Chạy trong execution phase
tasks.register("good") {
doLast {
val result = heavyComputation()
println(result)
}
}
tasks.register("lazyTask") {
// Property được evaluate lazily
val version = project.providers.gradleProperty("version")
doLast {
println("Version: ${version.get()}")
}
}

Type Mô tả
Copy Copy files
Delete Delete files/directories
Exec Execute external command
JavaExec Execute Java class
Zip Create ZIP archive
Jar Create JAR file
Test Run tests
// Delete task
tasks.register<Delete>("cleanGenerated") {
delete(layout.buildDirectory.dir("generated"))
}
// Exec task
tasks.register<Exec>("runScript") {
commandLine("bash", "scripts/deploy.sh")
}
// Zip task
tasks.register<Zip>("packageDocs") {
from("docs/")
archiveFileName.set("documentation.zip")
destinationDirectory.set(layout.buildDirectory.dir("archives"))
}