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.
Giới thiệu về Gradle Tasks
Phần tiêu đề “Giới thiệu về Gradle Tasks”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.
Xem danh sách tasks
Phần tiêu đề “Xem danh sách tasks”# Tất cả tasks./gradlew tasks
# Tasks với mô tả./gradlew tasks --all
# Tasks của module cụ thể./gradlew :app:tasksTasks phổ biến
Phần tiêu đề “Tasks phổ biến”| 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 |
Tạo Custom Task đơn giản
Phần tiêu đề “Tạo Custom Task đơn giản”Task cơ bản
Phần tiêu đề “Task cơ bản”// build.gradle.kts (module)tasks.register("hello") { group = "custom" description = "A simple hello task"
doLast { println("Hello from Gradle!") }}Chạy task:
./gradlew helloTask với doFirst và doLast
Phần tiêu đề “Task với doFirst và doLast”tasks.register("greeting") { doFirst { println("Preparing to greet...") } doLast { println("Hello, World!") } doLast { println("Goodbye!") }}📝 Lưu ý:
doFirstchạy trước,doLastchạy sau. Có thể có nhiềudoLastblocks và chúng chạy theo thứ tự khai báo.
Task Dependencies
Phần tiêu đề “Task Dependencies”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:#e8f5e9dependsOn
Phần tiêu đề “dependsOn”Task phụ thuộc vào task khác:
tasks.register("buildAll") { group = "build" description = "Build all variants"
dependsOn("assembleDebug", "assembleRelease")}finalizedBy
Phần tiêu đề “finalizedBy”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")}mustRunAfter / shouldRunAfter
Phần tiêu đề “mustRunAfter / shouldRunAfter”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"Task với Inputs và Outputs
Phần tiêu đề “Task với Inputs và Outputs”Gradle sử dụng inputs/outputs để xác định task có cần chạy lại hay không (incremental builds).
Ví dụ: Copy task
Phần tiêu đề “Ví dụ: Copy task”tasks.register<Copy>("copyDocs") { group = "documentation" description = "Copy documentation to output"
from("docs/") into(layout.buildDirectory.dir("documentation"))
include("*.md", "*.txt") exclude("**/draft/**")}Ví dụ: Task với inputs/outputs tùy chỉnh
Phần tiêu đề “Ví dụ: Task với inputs/outputs tùy chỉnh”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"))}Task thực tế
Phần tiêu đề “Task thực tế”1. Clean và Build
Phần tiêu đề “1. Clean và Build”tasks.register("cleanBuild") { group = "build" description = "Clean and build release APK"
dependsOn("clean", "assembleRelease")
tasks.findByName("assembleRelease")?.mustRunAfter("clean")}2. Print Build Info
Phần tiêu đề “2. Print Build Info”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()}") }}3. Check Dependencies
Phần tiêu đề “3. Check Dependencies”tasks.register("checkOutdatedDeps") { group = "verification" description = "Check for outdated dependencies"
doLast { exec { commandLine("./gradlew", "dependencyUpdates", "-Drevision=release") } }}4. Generate Build Report
Phần tiêu đề “4. Generate Build Report”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}") }}5. APK Size Check
Phần tiêu đề “5. APK Size Check”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!") } } }}Sử dụng Plugin để tổ chức Tasks
Phần tiêu đề “Sử dụng Plugin để tổ chức Tasks”Khi có nhiều custom tasks, nên tổ chức chúng trong một plugin:
buildSrc Plugin
Phần tiêu đề “buildSrc Plugin”- Tạo thư mục
buildSrc/:
buildSrc/├── build.gradle.kts└── src/main/kotlin/ └── CustomTasksPlugin.ktbuildSrc/build.gradle.kts:
plugins { `kotlin-dsl`}
repositories { mavenCentral()}CustomTasksPlugin.kt:
import org.gradle.api.Pluginimport 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}") } } } }}- Apply trong
build.gradle.kts:
apply<CustomTasksPlugin>()Best Practices
Phần tiêu đề “Best Practices”1. Luôn định nghĩa group và description
Phần tiêu đề “1. Luôn định nghĩa group và description”tasks.register("myTask") { group = "custom" // Giúp phân loại trong ./gradlew tasks description = "What this task does" // Mô tả rõ ràng}2. Sử dụng inputs/outputs cho caching
Phần tiêu đề “2. Sử dụng inputs/outputs cho caching”tasks.register("processData") { inputs.file("data/input.json") outputs.file(layout.buildDirectory.file("output.json"))
doLast { // Process data }}3. Tránh logic nặng trong configuration phase
Phần tiêu đề “3. Tránh logic nặng trong configuration phase”// ❌ Chạy trong configuration phasetasks.register("bad") { val result = heavyComputation() // Chạy mỗi lần sync doLast { println(result) }}
// ✅ Chạy trong execution phasetasks.register("good") { doLast { val result = heavyComputation() println(result) }}4. Sử dụng lazy configuration
Phần tiêu đề “4. Sử dụng lazy configuration”tasks.register("lazyTask") { // Property được evaluate lazily val version = project.providers.gradleProperty("version")
doLast { println("Version: ${version.get()}") }}Các Task Types có sẵn
Phần tiêu đề “Các Task Types có sẵn”| 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 tasktasks.register<Delete>("cleanGenerated") { delete(layout.buildDirectory.dir("generated"))}
// Exec tasktasks.register<Exec>("runScript") { commandLine("bash", "scripts/deploy.sh")}
// Zip tasktasks.register<Zip>("packageDocs") { from("docs/") archiveFileName.set("documentation.zip") destinationDirectory.set(layout.buildDirectory.dir("archives"))}