สร้าง Gradle Plugin ด้วย Kotlin เพื่อใช้งานบน Android - Getting Started

หลังจากเข้าใจโครงสร้างของ Android Gradle Plugin เบื้องต้นแล้ว สิ่งที่ต้องทำก่อนที่จะเขียน Gradle Plugin หรือ Convention Plugin ก็คือการเตรียมโปรเจคให้พร้อมเสียก่อน

เพื่อความเข้าใจง่าย เจ้าของบล็อกจะขอเรียกว่า Gradle Plugin แทน Convention Plugin

บทความในชุดเดียวกัน

สำหรับการทำตามขั้นตอนต่าง ๆ ในบทความนี้ แนะนำให้สร้างโปรเจคขึ้นมาใหม่เพื่อทำตามและทำความเข้าใจเสียก่อน แล้วค่อยลองทำกับในโปรเจคแอนดรอยด์ที่ต้องการในภายหลัง

เมื่อพร้อมแล้ว ก็เริ่มกันได้เลย

เพิ่ม buildSrc เข้าไปในโปรเจค

อย่างที่บอกไปในบทความแรกสุดของบทความชุดนี้ เจ้าของบล็อกจะสร้าง Gradle Plugin ไว้ใน buildSrc ของโปรเจคแอนดรอยด์ เพราะต้องการใช้แค่ภายในโปรเจคเท่านั้น

buildSrc เป็น Precompiled Script ที่จะทำงานก่อนที่ Gradle จะ Build Project ทำให้เราสามารถเตรียมคำสั่งให้กับ Build Script ที่ต้องการใช้งานในโปรเจคได้

โดยให้สร้าง buildSrc พร้อมกับ build.gradle.kts ไว้ข้างในนั้น เพื่อย้ายคำสั่งบางส่วนใน settings.gradle.kts และ build.gradle.kts ของโปรเจคไปไว้ในนั้นแทน

ย้ายคำสั่งบางส่วนใน settings.gradle.kts และ build.gradle.kts เข้าไปไว้ใน buildSrc

สมมติว่าใน settings.gradle.kts และ build.gradle.kts ของโปรเจคมีคำสั่งเกี่ยวกับ Gradle Plugin เป็นแบบนี้

// settings.gradle.kts (Project)
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
/* ... */

// build.gradle.kts (Project)
plugins {
    id("com.android.application") version "8.1.2" apply false
    id("org.jetbrains.kotlin.android") version "1.8.10" apply false
}

buildscript {
    dependencies {
        classpath("com.google.gms:google-services:4.4.0")
    }
}
/* ... */

ให้ย้าย pluginManagement {..} ใน settings.gradle.kts ไปไว้ใน buildSrc/build.gradle.kts แบบนี้แทน

// build.gradle.kts (buildSrc)
repositories {
    google()
    mavenCentral()
    gradlePluginPortal()
}

plugins {
    `kotlin-dsl`
}
ใน build.gradle.kts ของ buildSrc จำเป็นต้องเพิ่ม kotlin-dsl ไว้ใน plugins {..} ด้วยเสมอ

ส่วน Gradle Plugin ที่ประกาศไว้ใน build.gradle.kts ของโปรเจคให้ย้ายมาประกาศไว้ในนี้ด้วยเช่นกัน

// build.gradle.kts (buildSrc)
/* ... */
dependencies {
    implementation("com.android.tools.build:gradle:8.1.2")
    implementation("org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.9.10")
    implementation("com.google.gms:google-services:4.4.0")
}

จุดสังเกตสำหรับ Gradle Plugin จากโค้ดตัวอย่างข้างบน ก็คือเราจะต้องแปลง ID ให้เป็น Classpath แล้วเปลี่ยนคำสั่ง classpath ให้เป็น implementation แทน ยกตัวอย่างเช่น

ID Classpath
com.android.application com.android.tools.build:gradle
com.android.library com.android.tools.build:gradle
org.jetbrains.kotlin.android org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin
com.google.gms.google-services com.google.gms:google-services
com.google.firebase.crashlytics com.google.firebase:firebase-crashlytics
com.google.firebase.firebase-perf com.google.firebase:firebase-perf

ถ้าไม่รู้ว่า Grald Plugin นั้น ๆ มี Classpath เป็นอะไร ให้ลองเช็คใน

โดยทั้ง 2 เว็ปนี้จะเป็นเว็ปที่รวบรวมข้อมูลของ Gradle Plugin และมีรายละเอียดต่าง ๆ เกี่ยวกับ Plugin ตัวนั้น ๆ รวมไปถึง Classpath ด้วย

สรุปโค้ดทั้งหมดในตอนนี้

// build.gradle.kts (buildSrc)
repositories {
    google()
    mavenCentral()
    gradlePluginPortal()
}

plugins {
    `kotlin-dsl`
}

dependencies {
    implementation("com.android.tools.build:gradle:8.1.2")
    implementation("org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.9.10")
    implementation("com.google.gms:google-services:4.4.0")
}

// build.gradle.kts (Project)
// ย้ายไปไว้ใน buildSrc หมดแล้ว

// settings.gradle.kts (Project)
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
/* ... */

เมื่อมาถึงขั้นตอนนี้แล้ว ควรจะกด Run App แล้วสามารถ Build Project ได้ปกติ ถ้ามีปัญหาใด ๆ ระหว่างนี้ให้ลองตรวจสอบโค้ดใหม่ทั้งหมดอีกครั้งก่อนที่จะไปขั้นตอนต่อไป

สร้าง Gradle Plugin หรือ Convention Plugin

นักพัฒนาสามารถเพิ่มโค้ดใน buildSrc ได้เหมือนกับการเขียนโค้ดปกติเลย เพียงแต่โค้ดที่อยู่ในนั้นจะเป็นโค้ดสำหรับ Gradle

ดังนั้นการสร้าง Plugin จึงสามารถสร้างไฟล์ไว้ในนั้นได้เลย ยกตัวอย่างเช่น เจ้าของบล็อกสร้างไฟล์สำหรับ Plugin เพิ่มเข้าไปแบบนี้

และเตรียมโค้ดไว้ข้างในแบบนี้

package com.akexorcist.sleepingforless.gradle

import org.gradle.api.Plugin
import org.gradle.api.Project

class DependencySharingConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        println("Hello from DependencySharingConventionPlugin")
        // Implement your custom Gradle plugin here
    }
}

โค้ดตัวอย่างนี้จะเป็นโค้ดเริ่มต้นสำหรับการสร้าง Gradle Plugin ใด ๆ ก็ตาม ซึ่งในตอนนี้จะยังไม่ได้ทำอะไรนอกไปจากแสดงข้อความผ่าน Log ด้วยคำสั่ง println

และนอกจากจะเตรียมโค้ดสำหรับ Gradle Plugin แล้ว จะต้องเพิ่มคำสั่งใน build.gradle.kts ของ buildSrc ด้วยเสมอ เพื่อกำหนด Plugin ID และ Classpath เพื่อให้นำไปใช้ในโปรเจคได้

จากตัวอย่างโค้ดก่อนหน้าจะต้องกำหนดค่าใน build.gradle.kts ของ buildSrc แบบนี้

// build.gradle.kts (buildSrc)
/* ... */
gradlePlugin {
    plugins {
        register("dependencySharingConventionPlugin") {
            id = "akexorcist.dependency-sharing.convention"
            implementationClass = "com.akexorcist.sleepingforless.gradle.DependencySharingConventionPlugin"
        }
    }
}

ให้ทำการ Sync Gradle ใหม่ เพียงเท่านี้ Gradle Plugin ตัวนี้ก็จะพร้อมนำไปใช้งานในโปรเจคแล้ว (ถ้าโค้ดถูกต้องนะ)

ให้ทดสอบการทำงานของ Gradle Plugin ด้วยการเพิ่มเข้าไปใน App Module แบบนี้

// build.gradle.kts (App Module)
plugins {
    /* ... */
    id("akexorcist.dependency-sharing.convention")
}
/* ... */

จากนั้นให้ Sync Gradle แล้วเปิด Log ในหน้าต่าง Build ดู ก็จะพบว่ามีข้อความจากคำสั่ง println แสดงขึ้นมาใน Log ด้วย

นั่นหมายความว่า Gradle Plugin ที่เราสร้างขึ้นมาเองพร้อมใช้งานแล้วนั่นเอง

สรุป

ขั้นตอนทั้งหมดในบทความนี้จะเป็นเพียงการสร้าง Gradle Plugin เปล่า ๆ ขึ้นมาโดยยังไม่ได้เพิ่มคำสั่งใด ๆ เข้าไป สำหรับการใช้คำสั่งต่าง ๆ เพื่อให้ Gradle Plugin ทำอะไรบางอย่างกับโปรเจคแอนดรอยด์จะขอพูดในบทความถัดไปแทน โดยจะแบ่งออกหลายบทความเพื่อยกตัวอย่างการนำไปประยุกต์ใช้งานในแต่ละแบบ เพื่อช่วยให้ผู้ที่หลงเข้ามาอ่านเข้าใจวิธีการใช้งาน Gradle Plugin หรือ Convention Plugin มากขึ้น