สร้าง Gradle Plugin ด้วย Kotlin เพื่อใช้งานบน Android - การสร้าง Firebase Plugin เพื่อแยกคำสั่งของ Firebase ออกจาก App Module
ในบทความนี้จะเป็นการสร้าง Gradle Plugin เพื่อกำหนดค่าต่าง ๆ สำหรับ Firebase Plugin แล้วนำไปใช้งานในโปรเจคของเรา ซึ่งจะมีข้อดีตรงที่เราสามารถแยก Build Logic ในส่วนนี้แยกออกมาได้ จากเดิมที่จะต้องรวมโค้ดเหล่านี้ไว้กับโค้ดอื่น ๆ ใน build.gradle.kts
บทความในชุดเดียวกัน
- Introduction
- Android Gradle Plugin
- Getting Started
- [ตัวอย่าง] สร้าง Dependency Sharing Plugin เพื่อใช้กับทุก Module
- [ตัวอย่าง] สร้าง Firebase Plugin เพื่อแยกคำสั่งของ Firebase ออกจาก App Module [Now Reading]
- [ตัวอย่าง] สร้าง Configuration Sharing Plugin เพื่อใช้งานใน Library Module
สำหรับการขั้นตอนเริ่มต้นในการสร้าง Gradle Plugin จะอยู่ในบทความ "สร้าง Gradle Plugin ด้วย Kotlin เพื่อใช้งานบน Android - Getting Started" (แนะนำให้อ่านก่อน)
โดยตัวอย่าง 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 จากภายนอกเข้ามาช่วยอำนวยความสะดวกตามสถานการณ์ที่เจอในระหว่างการพัฒนาแอป ดังนั้นถ้าเราแยกโค้ดบางส่วนออกจากกันอย่างอิสระได้ ก็จะช่วยให้เราดูแลโค้ดเหล่านี้ในอนาคตได้ง่ายกว่า