สร้าง Gradle Plugin ด้วย Kotlin เพื่อใช้งานบน Android - การสร้าง Firebase Plugin เพื่อแยกคำสั่งของ Firebase ออกจาก App Module

ในบทความนี้จะเป็นการสร้าง Gradle Plugin เพื่อกำหนดค่าต่าง ๆ สำหรับ Firebase Plugin แล้วนำไปใช้งานในโปรเจคของเรา ซึ่งจะมีข้อดีตรงที่เราสามารถแยก Build Logic ในส่วนนี้แยกออกมาได้ จากเดิมที่จะต้องรวมโค้ดเหล่านี้ไว้กับโค้ดอื่น ๆ ใน build.gradle.kts

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

สำหรับการขั้นตอนเริ่มต้นในการสร้าง Gradle Plugin จะอยู่ในบทความ "สร้าง Gradle Plugin ด้วย Kotlin เพื่อใช้งานบน Android - Getting Started" (แนะนำให้อ่านก่อน)

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

โดยตัวอย่าง Firebase Plugin และ Dependency ที่จะใช้ในบทความนี้มีทั้งหมด 2 ตัวด้วยกันคือ Firebase Crashlytics และ Firebase Performance

มาเริ่มกันเถอะ

ให้เพิ่ม Dependency ของ Firebase Plugin ใน build.gradle.kts ใน buildSrc เป็นอย่างแรกสุดก่อน

// build.gradle.kts (buildSrc)
/* ... */
dependencies {
    /* ... */
    implementation("com.google.gms:google-services:4.4.0")
    implementation("com.google.firebase:perf-plugin:1.4.2")
    implementation("com.google.firebase:firebase-crashlytics-gradle:2.9.9")
}

จากนั้นให้สร้างไฟล์สำหรับ Gradle Plugin ขึ้นมาตามใจชอบ โดยเจ้าของบล็อกขอตั้งชื่อว่า FirebaseConventionPlugin และเตรียมโค้ดเริ่มต้นไว้แบบนี้

// buildSrc/src/main/kotlin/com/akexorcist/sleepingforless/gradle/FirebaseConventionPlugin.kt

package com.akexorcist.sleepingforless.gradle

import com.android.build.api.dsl.ApplicationExtension
import com.google.firebase.perf.plugin.FirebasePerfExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies

class FirebaseConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            // Add build logic for Firebase plugin here
        }
    }
}

ทำการเพิ่ม Firebase Plugin ผ่าน pluginManager เข้าไปแบบนี้

// buildSrc/src/main/kotlin/com/akexorcist/sleepingforless/gradle/FirebaseConventionPlugin.kt

class FirebaseConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            with(pluginManager) {
                apply("com.google.gms.google-services")
                apply("com.google.firebase.firebase-perf")
                apply("com.google.firebase.crashlytics")
            }
        }
    }
}

และเนื่องจาก Firebase Performance และ Firebase Crashlytics ต้องเพิ่ม Dependency ให้กับ Module ที่จะเอาไปใช้งานด้วย ก็ให้เพิ่มต่อท้ายเข้าไปแบบนี้

// buildSrc/src/main/kotlin/com/akexorcist/sleepingforless/gradle/FirebaseConventionPlugin.kt

class FirebaseConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            /* ... */
            dependencies {
                "implementation"("com.google.firebase:firebase-crashlytics-ktx:18.2.0")
                "implementation"("com.google.firebase:firebase-perf-ktx:20.4.1")
            }
        }
    }
}

สำหรับ Firebase Performance จะสามารถกำหนดค่าบางอย่างใน Gradle ได้ ยกตัวอย่างเช่น ปิดทำงานของ Firebase Performance สำหรับ Debug Build Type ที่ปกติจะประกาศไว้ใน build.gradle.kts ของ App Module แบบนี้

// build.gradle.kts (App Module)
import com.google.firebase.perf.plugin.FirebasePerfExtension

/* ... */
android {
    /* ... */
    buildTypes {
        debug {
            configure<FirebasePerfExtension> {
                setInstrumentationEnabled(false)
            }
        }
    }
}

เมื่อย้ายคำสั่งดังกล่าวมาอยู่ใน Gradle Plugin ของเราเองก็จะเปลี่ยนเป็นแบบนี้แทน

// buildSrc/src/main/kotlin/com/akexorcist/sleepingforless/gradle/FirebaseConventionPlugin.kt

class FirebaseConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            /* ... */
            extensions.configure<ApplicationExtension> {
                buildTypes.configureEach {
                    if (name == "debug") {
                        configure<FirebasePerfExtension> {
                            setInstrumentationEnabled(false)
                        }
                    }
                }
            }
        }
    }
}

โดยเราจะต้องเช็คก่อนว่าใน extensions มี ApplicationExtension หรือไม่ จากนั้นให้หา Debug Build Type เพื่อกำหนดค่าสำหรับ Firebase Performance ผ่าน Configure ที่ชื่อว่า FirebasePerfExtension จากนั้นเราก็จะเรียกคำสั่งที่อยู่ข้างใน Extension ดังกล่าวได้ตามต้องการ

และจากโค้ดทั้งหมดใน FirebaseConventionPlugin ที่เจ้าของบล็อกยกตัวอย่างขึ้นมา เมื่อรวมโค้ดทั้งหมดเข้าด้วยกันก็จะได้ออกมาเป็นแบบนี้

// buildSrc/src/main/kotlin/com/akexorcist/sleepingforless/gradle/FirebaseConventionPlugin.kt

package com.akexorcist.sleepingforless.gradle

import com.android.build.api.dsl.ApplicationExtension
import com.google.firebase.perf.plugin.FirebasePerfExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies

class FirebaseConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            with(pluginManager) {
                apply("com.google.gms.google-services")
                apply("com.google.firebase.firebase-perf")
                apply("com.google.firebase.crashlytics")
            }

            extensions.configure<ApplicationExtension> {
                buildTypes.configureEach {
                    if (name == "debug") {
                        configure<FirebasePerfExtension> {
                            setInstrumentationEnabled(false)
                        }
                    }
                }
            }

            dependencies {
                "implementation"("com.google.firebase:firebase-crashlytics-ktx:18.2.0")
                "implementation"("com.google.firebase:firebase-perf-ktx:20.4.1")
            }
        }
    }
}

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

// build.gradle.kts (buildSrc)
/* ... */
gradlePlugin {
    plugins {
        register("firebaseConventionPlugin") {
            id = "akexorcist.firebase.convention"
            implementationClass = "com.akexorcist.sleepingforless.gradle.FirebaseConventionPlugin"
        }
    }
}

จากนั้นก็ให้เพิ่ม Gradle Plugin ของเราเข้าไปใน App Module ได้เลย

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

เพียงเท่านี้ก็เป็นอันเสร็จเรียบร้อย

สรุป

จะเห็นว่าการทำแบบนี้จะช่วยให้นักพัฒนาสามารถแยกโค้ดที่เกี่ยวกับ Firebase Plugin ออกมาจาก App Module ได้อย่างอิสระ ไม่ต้องกลัวว่าโค้ดเหล่านี้จะไปปะปนอยู่กับโค้ดอื่น ๆ ใน build.gradle.kts ของ App Module เพราะในโปรเจคที่มีขนาดใหญ่ประมาณนึงจะเริ่มมีการใช้ Gradle Plugin จากภายนอกเข้ามาช่วยอำนวยความสะดวกตามสถานการณ์ที่เจอในระหว่างการพัฒนาแอป ดังนั้นถ้าเราแยกโค้ดบางส่วนออกจากกันอย่างอิสระได้ ก็จะช่วยให้เราดูแลโค้ดเหล่านี้ในอนาคตได้ง่ายกว่า