หนีความวุ่นวายด้วยการทำ Product Flavour ในแอปแอนดรอยด์
ไม่ใช่แอปแอนดรอยด์รสชาติต่างๆ แต่เป็นการ build ตอนทำ staging และ production ต่างหากหล่ะ!
เคยไหม ที่แอปเราทดสอบ feature ต่างๆตอน staging ก่อนที่แอปเราจะออกสู่ production แล้ว path ของ API ที่ทดสอบตอน develop กับ production คนละ path กัน (ก็แหงสิ)
เคยไหม ที่เราทำ config ตอน develop และ production ออกจากกัน เพื่อไม่ให้มันปนกัน
เราคงไม่อยากแก้โค้ดอะไรแบบยุกยับบ่อยๆเนอะ
ดังนั้นมีตัวช่วย นั่นคือ การทำ Product Flavors นั่นเอง
แล้ว Product Flavors คืออะไรหล่ะ?
เกริ่นก่อน มันจะมี 2 แบบ คือ Build Type ก็คือการ set config ในระดับ module ซึ่งมันจะแถมมาโดยปกติในทุกครั้งที่เราสร้างโปรเจกใหม่ เอาง่ายๆ มีทุกโปรเจกเลย ซึ่งจะแบ่งมาให้เป็น debug (ตอนที่เราเสียบ device แล้วรัน) กับ release (บน store อ่ะ)
แต่เจ้า Product Flavors นั่นคือ environment ต่างๆภายใน project เช่น ตอน staging กับ production ใช้ server คนละตัวกัน ดังนั้นจะต้อง config อะไรบางอย่างให้สามารถรันได้ทั้ง staging และ production
เริ่มต้นการทำ Product Flavors อย่างไรดี
ทำได้ 2 แบบนะ
แบบแรก make sure for save my life ไปที่ Build -> Edit Flavors… แล้วสร้างเพิ่ม ใช้ชื่อว่า developer กับ production
หรือแบบที่สองใส่แบบดิบๆไปเลย ซึ่งได้ผลเหมือนวิธีแรก ที่ build.gradle ของ app
android {
...
defaultConfig {
...
}
buildTypes {
...
}
productFlavors {
develop {}
production {}
}
}
แน่นอนว่า มันก็พังจ้า เพราะเราไม่ได้ define dimension ของเจ้า flavor ไว้นั่นเอง
All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html
ดังนั้นเราจะเพิ่ม dimension แต่ละอัน บน Android Studio 3.1.3 ขึ้นไป แบบนี้
จากนั้นเราจะ sync gradle ผ่านอย่างสดใสเหมือนรอยยิ้มของคุณนุ้ย
สุดท้ายถ้ายังเหลือชีวิตจากรอยยิ้มคุณนุ้ย ก็จะได้ผลแบบนี้
ปล. ใส่แบบนี้ไม่ได้นะเออ
productFlavors {
flavorDimensions("develop", "production")
develop {
dimension "develop"
}
production {
dimension "production"
}
}
ในที่นี้เราจะ sample ให้ดู 3 อย่างที่เราต้องทำ คือ
- set url ของตัว backend
- แยก package name ของ staging และ production ออกจากกัน
- ใส่ไฟล์ config ของเจ้า firebase นามว่า google-service.json เนื่องจากเราอยากแยกโปรเจกตอนที่เรา develop กับตอนที่เราปล่อยแอปเราออกจากกัน เพราะกลัวข้อมูลมันจะตีกันนั่นเอง เช่น Crashlytics ซึ่งมีทั้งตอนเราทำ และของ user เพียวๆ เป็นต้น
Set Url ของเจ้า Backend
เราต้องวางแผนอยู่แล้วเนอะว่าตัวแปรไหนที่จะเปลี่ยนตาม Product Flavors ในขั้นนี้เราจะใช้คนๆนึงมาช่วย นามว่า BuildConfig นั่นเอง เจ้านี้มันเป็น class ที่เอาไปใช้ build variants ที่เราต้องการ และเราไม่สามารถสร้างเองได้ เพราะมันอยู่ภายใต้ build folder นั่นเองงงงง
ค่อยๆเริ่มไปทีละนิด บอกเลยว่าคราวก่อนน้องในทีมทำ ใช้เวลาทำความเข้าใจนานอยู่ เราเลยจะย่อยให้ทำตามกันง่ายๆ งานจะได้เสร็จเร็วขึ้นเนอะ
ไปที่ build.gradle ที่เราค้างไว้ของ app ใส่เจ้า buildConfigField ไว้ภายใต้ product flavors ที่สร้างไว้เมื่อกี้
buildConfigField("String", "API_BASE", "\"http://localhost:3000\"")
parameter แต่ละตัวบอกอะไรเราได้บ้าง
- String type : เป็น type ของตัวแปรที่เราจะสร้าง
- String name : ชื่อตัวแปร
- String value : ค่าของตัวแปร
ทั้งหมดทั้งมวลเป็นแบบนี้
การนำไปใช้ ตอนแรกลองเอาไปใช้ จะเห็นแค่นี้ ไม่เห็นตัวที่เราเพิ่งสร้าง
ต้อง sync gradle เสียก่อนจึงจะเห็น
ดังนั้นเรานำไปเรียกใช้ในภาษา Kotlin ได้แบบนี้
const val BASE_PATH = BuildConfig.API_BASE
และ import ตัวนี้เท่านั้นนะเออ
import <package_name>.BuildConfig
เวลาใช้งาน ก็แค่เลือกเจ้า build variant แล้ว Run “app” ก็ได้แล้วจ้าา เย้ และถ้าอยากสร้างหลายตัวก็ย่อมได้ สร้างเรียงต่อกันลงมาตามปกติสุข
ถ้าเรา Generate Signed APK… ไฟล์ apk ที่ได้ ก็จะอยู่แถวๆนี้แหละ เช่น ของ production จะอยู่ที่ app/production/release/app-production-release.apk แบบในรูปนี้ ส่วน release ก็คือของ default ปกตินั่นแหละ
ถ้าอยากมีแอป Staging กับ Production แยกกันหล่ะ?
ทำได้นะ โดยการต่อท้ายไปข้างหลังของ package name เดิมที่มีอยู่ และเราสามารถแยกพวก value ต่างๆออกจากกัน โดยเฉพาะชื่อแอปด้วยจะได้ไม่สับสนว่านี่คืออะไร โดยใส่เพิ่มไปดังนี้
productFlavors {
flavorDimensions("develop")
develop {
applicationIdSuffix ".develop"
...
resValue "string", "app_name", "Application Demo"
}
flavorDimensions("production")
...
resValue "string", "app_name", "Application"
}
}
- applicationIdSuffix ก็คือ ชื่อห้อยต่อท้าย package ที่มีอยู่นั่นเอง สมมุติ package name คือ com.mikkipastel.app พอใส่อันนี้อัน staging ก็จะเป็น com.mikkipastel.app.develop นั่นเอง
- resValue อันนี้ก็จะเป็น resource value ต่างๆ ในที่นี้ เราจะอ้างอิงถึง string ที่มีชื่อว่า app_name ให้มีค่าเป็น Application Demo สำหรับ staging และมีค่าเป็น Application สำหรับ production นั่นคือเป็นการแยกชื่อแอปของ staging และ production นั่นเอง
แยก Configuration File ของ Firebase
เราสร้าง project บน Firebase และติดตั้งตอนที่ทำแอปแรกๆเลย และอาจจะมีความสับสนหลังจากแอปออกไปแล้ว ว่าสิ่งที่ Firebase เก็บมา มันเป็นช่วง staging ที่เราทำ version ใหม่หลังจากปล่อย release version ออกไปแล้วหรือไม่
ซึ่งทีมเราทำการศึกษาค้นคว้า วิจัย ออกมาแล้วว่า การสร้างโปรเจกใน Firebase แยกกัน แล้วเอาไฟล์ config มายัดนั้น เป็นวิธีที่ใครๆเขาก็ทำกัน……………………….. ไม่ได้แปลกประหลาดแก่ประการใด ขนาดใน Document ของ Firebase ยังบอกเลยจ้า (แน่นอนทำได้ทั้ง Android และ iOS เลย)
Configure Multiple Projects | Firebase
Sometimes you need to access different projects using the same APIs - for example, accessing multiple database…firebase.google.com
ขั้นตอนการทำ สร้างโปรเจกใน Firebase เพิ่มมาอีกอันนึง แบ่งดีๆว่าอันไหน staging อันไหน production นะ จากนั้นนำไฟล์ google-service.json ไปใส่ในแต่ละ project flavour
เมื่อกี้เราสร้าง project flavour ที่ชื่อว่า develop กับ production ไปใช่ม๊า ดังนั้นเราจะใส่ไป
app/src/develop/google-services.json
app/src/production/google-services.json
อ้างอิงสถานที่การใส่ไฟล์ config ของ Firebase เพิ่มตามนี้เลยจ้า
The Google Services Gradle Plugin | Google APIs for Android | Google Developers
A: The Firebase console will help you download the google-services.json. In addition, the Quickstart guides for most…developers.google.com
ใน Document ของ Firebase บอกให้ไปลบเจ้า google-service.json
ที่ root project คือจะอยู่ใน module app ออก และลบ apply plugin: ‘com.google.gms.google-services’
ที่ build.gradle
ของ app ด้วย ซึ่งบอกเลยว่า ทำตามเขาแล้ววุ่นวายมาก เพราะ build ไม่ผ่านหล่ะสิ เจอแบบนี้ทำตัวไม่ถูกเลย
Crashlytics found an invalid API key: null.
Check the Crashlytics plugin to make sure that the application has been added successfully!
Contact [email protected] for assistance.
สรุป ใส่แค่ไฟล์ google-services.json
เพิ่มในแต่ละ flavour จากนั้นจะ build ผ่านอย่างสดใสดั่งรอยยิ้มคุณนุ้ย
โอเค จากนั้นมา init firebase project ของเรากันเถอะ
เราสร้าง function แยกมาอันนึง ใช้ตอนเข้าแอปเรา ถ้าอ่านจาก doc รวมๆกันจะเป็นแบบนี้ ขอเขียนเป็น Kotlin น๊าาา
โปรเจกเรามี Analytics for Firebase ด้วย ดังนั้นต้องมาสร้างตัวแปรเหมือนตอนแยก server เลย แบบนี้
สุดท้าย การนำไปใช้จริง บนโปรเจกที่เป็น Kotlin จะเป็นเช่นนี้แหละ
val option = FirebaseOptions.Builder()
.setApplicationId(BuildConfig.FIREBASE_APP_ID)
.build()
FirebaseApp.initializeApp(this, option, "default")
สุดท้ายไป run บนเครื่อง แอปเราจะใช้ Firebase ได้ทั้ง staging และ production แล้ว เย้ เอาหลักฐานมาให้ดูเลยยยย
ข้อดีนอกจากแยก data แล้ว user ก็อาจจะไม่ต้องรำคาญตอนเราลองยิง FCM ด้วยนะเออ ฮ่าาาๆๆๆๆๆ
ผ่านไปแล้วสำหรับการสร้าง Product Flavour หวังว่าเป็นประโยชน์แก่คนที่ผ่านไปผ่านมาเน้อ แบบอ่านแล้วเอาไปใช้ได้เลย เย้ๆๆๆๆ
พื้นที่โฆษณา อยากฟังเพลง cover เพราะๆมาที่ Song Shakes ได้เลยจ้า
สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ