หนีความวุ่นวายด้วยการทำ Product Flavour ในแอปแอนดรอยด์

Android Oct 5, 2018

ไม่ใช่แอปแอนดรอยด์รสชาติต่างๆ แต่เป็นการ build ตอนทำ staging และ production ต่างหากหล่ะ!

Photo by Tom Hermans on Unsplash

เคยไหม ที่แอปเราทดสอบ 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 ขึ้นไป แบบนี้

productFlavors {
    flavorDimensions("develop")
    develop {
        ...
    }

    flavorDimensions("production")
    production {
        ...
    }
}
https://tenor.com/YHO3.gif

จากนั้นเราจะ 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” ก็ได้แล้วจ้าา เย้ และถ้าอยากสร้างหลายตัวก็ย่อมได้ สร้างเรียงต่อกันลงมาตามปกติสุข

Configure build variants | Android Developers
Find out how you can configure build variants to create different versions of your app from a single project.
Building multiple flavors of an Android app

ถ้าเรา Generate Signed APK… ไฟล์ apk ที่ได้ ก็จะอยู่แถวๆนี้แหละ เช่น ของ production จะอยู่ที่ app/production/release/app-production-release.apk แบบในรูปนี้ ส่วน release ก็คือของ default ปกตินั่นแหละ

ใน folder ของ app จะมี folder ใหม่ที่ชื่อว่า production และ develop ที่เพิ่มมาหลังจากการสร้าง product flavour

ถ้าอยากมีแอป 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 ได้เลยจ้า

Song Shakes - Music Contest Watch Video, Free - Apps on Google Play
Song Shakes is a cover music community. Every Singer and Musicians are welcome to join our contests. On the other hand, the viewers can join the fun by voting for their favorite contestants. Song Shakes is a place to showcase for cover musicians from all ages, and of course, it’s free! No more audi…

สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ

อย่าลืมกด like กด share บทความกันด้วยนะคะ :)

Posted by MikkiPastel on Sunday, 10 December 2017

Tags

Minseo Chayabanjonglerd

I am a full-time Android Developer and part-time contributor with developer community and web3 world, who believe people have hard skills and soft skills to up-skill to da moon.