มาเปลี่ยน Push Notification บน Android จาก GCM ไปเป็น FCM กัน
ลาก่อน GCM, สวัสดี FCM
ย้อนไปเมื่อหลายก่อน ถ้าคุณอยากจะทำ Push Notification ในแอพแอนดรอยด์ ก็คงไม่พ้น Google Cloud Messaging (GCM) ที่เป็นบริการส่งข้อความอยาก Web Server ของเราไปให้ผู้ใช้ได้อย่างง่ายดาย
จนกระทั่งวันที่ Google ได้เปิดตัว Firebase ขึ้นมาซึ่งในนั้นมี Firebase Cloud Messaging (FCM) อยู่ด้วยอีกทั้งยังแนะนำให้นักพัฒนาเปลี่ยนจาก GCM ไปใช้เป็น FCM เพื่ออนาคตที่ดีกว่า
มาถึงวันนี้ GCM ใกล้จะหมดอายุขัยในอีกไม่นาน (ประกาศ Deprecated ในวันที่ 4 ตุลาคม 2018 และปิดให้บริการในวันที่ 11 เมษายน 2019) แต่ก็ยังมีนักพัฒนาอีกมากมายที่ยังคงใช้ GCM กันอยู่
ดังนั้นใครที่ยังใช้ GCM อยู่ก็จะต้อง Migrate ระบบไปใช้เป็น FCM ให้เรียบร้อยซะ ก่อนที่ GCM จะปิดให้บริการอย่างถาวร
อะไรนะ ยังไม่รู้ว่าจะ Migrate ยังไง? ถ้างั้นก็อ่านบทความนี้ต่อได้เลย
สิ่งที่นักพัฒนาควรรู้ก่อนจะย้าย Push Notification จาก GCM ไปใช้ FCM
ฝั่ง Android
- Device Token ที่ใช้บน GCM สามารถเอาใช้งานบน FCM ได้ทันที และ Device Token ก็ยังคงมีโอกาสเปลี่ยนได้ตลอดเวลาเหมือนเดิม
- Topic Subscription บน GCM สามารถทำงานบน FCM ได้ทันที ไม่จำเป็นต้อง Subscribe ใหม่
ฝั่ง Web Server
- FCM รองรับการใช้งานผ่าน HTTP และ XMPP เหมือนกับ GCM ดังนั้นแทบจะไม่ต้องทำอะไรเพิ่มเติมนอกจากเปลี่ยน Endpoint มาใช้ของ FCM เท่านั้นเอง
ขั้นตอนการ Migrate จาก GCM ไปเป็น FCM (ฝั่ง Android)
เพิ่มโปรเจคเก่าเข้าไปใน Firebase Console
การเพิ่มโปรเจคบน Google API Console ให้ไปอยู่ใน Firebase Console นั้นทำได้ง่ายมาก เพราะแค่กดสร้างโปรเจคขึ้นมาใหม่ใน Firebase Console ก็จะมีรายชื่อโปรเจคเก่าๆที่อยู่ใน Google API Console ให้เลือกด้วย
ถ้าเลือกโปรเจคเก่าๆที่อยู่บน Google API Console ก็จะเป้นการ Import ตัวโปรเจคเข้าสู่ Firebase Console โดยที่ API บางส่วนบน Google API ก็ยังสามารถใช้งานควบคู่กันได้ตามปกติ ยกตัวอย่างเช่นโปรเจคของผมที่มีบริการต่างๆของ Google API และ Firebase ดังนี้
- Google API Console : Google Maps API และ Google Direction API
- Firebase Console : Firebase Analytics และ Firebase Cloud Messaging
ซึ่งทั้งหมดนี้อยู่ในโปรเจคตัวเดียวกันล่ะ ดังนั้นผมจึงสามารถใช้บริการของทั้งสองฝั่งโดยอยู่ในโปรเจคตัวเดียวกันได้เลย เย้
เตรียม google-services.json ให้พร้อม
จากนั้นก็ให้เข้าไปที่ Project settings
ของโปรเจคตัวนั้นต่อ เพราะว่าเราจะต้องไปดาวน์โหลดไฟล์ google-services.json
เพื่อเอาไปใช้ในฝั่ง Android แทนไฟล์ของเก่าที่ได้มาจาก GCM
เมื่อเข้าหน้า Project settings แล้ว ในแถบ General จะมีรายละเอียดของโปรเจคอยู่ รวมไปถึง Web API Key เพื่อให้ฝั่ง Web Server เอาไปใช้แทน API Key ตัวเก่าที่ได้จาก GCM (ที่ต้องแนบไปใน Header ที่ชื่อว่า Authorization)
และเมื่อเลื่อนลงมาข้างล่างอีกนิดก็จะเป็นส่วนที่เราจะต้องเพิ่มรายละเอียดแอพของเราเข้าไปเพื่อให้ Firebase Console สร้างไฟล์ google-service.json
ขึ้นมา
โดยกดที่ปุ่ม Add app
แล้วเลือกเป็น Android ซะ จากนั้นจะมีหน้าต่างที่แบ่งออกเป็น 4 ตอน
โดยขั้นตอนที่ 1 จะต้องกรอกข้อมูลของแอพให้เรียบร้อยซะ มีดังนี้
- Package Name
- SHA-1 Certificate Fingerprint
จากนั้นในขั้นตอนที่ 2 จะให้เราดาวน์โหลดไฟล์ google-services.json
ไปใช้งาน
และขั้นตอนที่ 3 ก็จะเป็นวิธีการเพิ่ม Firebase SDK เข้าไปในโปรเจค ซึ่งข้ามไปได้เลย
ส่วนขั้นตอนที่ 4 จะเป็นการเช็คว่าสามารถใช้งาน Firebase ได้ปกติหรือป่าว เราสามารถข้ามขั้นตอนนี้ได้ จะมีปุ่มให้กดข้ามอยู่
เพิ่มเติม — ส่วนใหญ่จะแบ่งโปรเจคเป็น 2 โปรเจค ตัวนึงสำหรับ Develop และอีกตัวสำหรับ Production ดังนั้นอย่าลืมเตรียม google-services.json แยกกันด้วยนะ โดยที่
- โปรเจคที่เป็น Develop ก็ให้ใช้ SHA-1 ของ Debug Keystore
- โปรเจคที่เป็น Production ก็ให้ใช้ SHA-1 ของ Signed Keystore แทน
เพราะว่า Firebase SDK นั้นรองรับ Build Variant อยู่แล้ว เราจึงสามารถใส่ google-services.json
แยกตาม Variant ได้ด้วย ตัวที่เป็น debug
ก็ให้ใช้ของโปรเจคที่เป็น Develop ซะ ส่วนตัว release
ก็ให้ใช้ของตัวโปรเจคที่เป็น Production แทน
แต่ถ้าคุณมีโปรเจคอยู่ตัวเดียวที่พร้อมจะใช้งานเป็นทั้ง Debug และ Release ก็ให้ใส่ google-services.json ไว้ใน /src/main
ได้เลย
เปลี่ยนไปใช้ Dependency ของ FCM
สำหรับ Classpath ใน Gradle ยังใช้ของตัวเดิมเหมือน GCM อยู่นะ
แต่ให้เปลี่ยน Dependency จาก GCM เป็น FCM ซะ
ส่วน apply plugin: "com.google.gms.google-services"
ก็ใส่ไว้ที่บรรทัดสุดท้ายของ build.gradle (Module) เหมือนเดิมนะ ไม่ต้องเปลี่ยนอะไร และไม่ต้องลบออก
เปลี่ยนโค้ดในแอพให้รองรับ FCM
ทีนี้ก็เป็นส่วนของโค้ดที่เขียนไว้ใน Android ที่ต้องปรับเล็กน้อยเพื่อให้เป็นของ FCM แทน
สมัยที่ใช้ GCM เราจะต้องสร้างคลาสขึ้นมา 3 ตัวด้วยกันดังนี้
- GcmListenerService — เพื่อรับ Push Notification ที่ส่งเข้ามา
- InstanceIDListenerService — เพื่อดักว่า Token มีการอัพเดทใหม่
- IntentService — เพื่อส่ง Token ตัวใหม่ขึ้น Web Server จะได้ใช้ Token ตัวใหม่แทนตัวเก่า
แต่เมื่อใช้ FCM จะยุบรวมเหลือเพียงตัวเดียวที่มีชื่อว่า FirebaseMessagingService
ถ้ายังนึกภาพไม่ออกว่าโค้ดจะเปลี่ยนไปยังไงบ้าง ให้ดูตามภาพข้างล่างนี้ได้เลย
จะเห็นว่าโค้ดหายไปเยอะมาก เหลือแค่โค้ดที่เราจำเป็นต้องเขียนเองเท่านั้น ซึ่งก็คือคำสั่งอัปโหลด Token ขึ้น Web Server และคำสั่งเมื่อมี Message จาก Push Notification ส่งเข้ามา
ไม่ต้องประกาศคำสั่งเรียก IntentService ไว้ที่ Activity ตัวแรกสุดของแอพแล้ว
ตอนที่ใช้ GCM จะต้องใส่คำสั่งเพื่อเรียกให้ IntentService ทำงานครั้งหนึ่งก่อน เพื่อให้ Token สร้างขึ้นมาใหม่
แต่พอมาใช้ FCM ก็ไม่จำเป็นอีกต่อไป เพราะว่า Firebase SDK จัดการให้หมดแล้ว ลบคำสั่งตรงนั้นทิ้งได้เลย
เมื่อ Token มีการอัปเดต
เมื่อไม่ต้องประกาศคำสั่งในหน้าแรกของ Activity เพื่อให้ GCM สร้าง Token ขึ้นมาใหม่ ดังนั้น Event จาก Token จะมี Behavior แตกต่างกันอยู่นิดหน่อย
GCM — AppRegistrationIntentService
ถูกเรียกทุกครั้งเมื่อเปิดแอพขึ้นมาใหม่ และเมื่อ Token มีการ Refresh ดังนั้นต้องมีการเขียนเช็คว่าเคยอัปโหลด Token ตัวนั้นขึ้น Web Server แล้วหรือยัง เพื่อป้องกันการอัปโหลด Token ซ้ำ
FCM — onNewToken()
ใน AppFirebaseMessagingService
จะถูกเรียกก็ต่อเมื่อ Token มีการอัปเดตใหม่เท่านั้น ดังนั้นเขียนคำสั่งอัปโหลด Token ขึ้น Web Server ก็พอ และคำสั่งเผื่อกรณีที่ส่งไม่สำเร็จด้วย (เผื่ออินเตอร์เนตมีปัญหา)
เมื่อได้รับ Message จาก Push Notification
ลักษณะของข้อมูลที่ส่งเข้าในเครื่องของผู้ใช้จะมีความแตกต่างกันระหว่าง GCM กับ FCM ดังนี้
GCM — Message จะถูกส่งเข้ามาใน onMessageReceived(from: String?, data: Bundle?)
โดยเช็คว่าส่งมาจากใครได้จากตัวแปร from
และข้อมูลที่ส่งมาจะอยู่ในรูปของ Bundle ที่ชื่อว่า data
FCM — Message จะถูกส่งเข้ามาใน onMessageReceived(remoteMessage: RemoteMessage?)
ซึ่งข้างใน RemoteMessage
จะมีให้ดังนี้
ถ้าอยากได้เหมือนเดิมแบบที่เคยเขียนไว้ใน GCM ก็ดึงแค่ from
และ data
มาใช้ก็พอ
แต่สังเกตดีๆจะเห็นว่า data
ที่ได้นั้นอยู่ในรูปของ Map<String, String> นะ ไม่ได้เป็น Bundle เหมือน GCM ดังนั้นตรงนี้ก็ต้องเปลี่ยนโค้ดกันนิดหน่อย
อย่าลืมลบ Permission, Service และ Broadcast Receiver ใน Android Manifest ด้วยนะ
จากของเดิมที่จะต้องประกาศ Wake Lock Permission, Receiver ของ GCM Receiver และ Service ทั้ง 3 ตัวที่สร้างขึ้นมาสำหรับ GCM
เมื่อเปลี่ยนเป็น FCM ซึ่งไม่จำเป็นต้องประกาศ Service และ Broadcast Receiver ให้เยอะแยะอีกต่อไป และไม่จำเป็นต้องขอ Wake Lock Permission แล้วด้วย ดังนั้นจะเหลือแค่นี้แทน
สบายตาขึ้นเยอะเลยเนอะ
ยังไม่พอ FCM ยังมี Meta-data สำหรับกำหนดการทำงานของ Notification ด้วยนะ ทำให้ไม่ต้องไปนั่งประกาศผ่านโค้ด Java/Kotlin แค่ประกาศไว้ใน Android Manifest ก็พอ
โดยที่
com.google.firebase.messaging.default_notification_icon
— เอาไว้กำหนด Default Icon เวลามี Notification Payload ส่งเข้ามา (สามารถกำหนดเป็น Icon ต่างๆได้ใน Payload)com.google.firebase.messaging.default_notification_color
— เอาไว้กำหนดสีของ Icon ที่ถูกสร้างจาก Notification Payloadcom.google.firebase.messaging.default_notification_channel_id
— เอาไว้กำหนด Default Channel ID ของ Notification (Channel ID เป็นฟีเจอร์ของ Android 8.0 Oreo ที่จะต้องกำหนดให้กับ Notification ทุกตัว)
เพียงเท่านี้ฝั่ง Android ก็พร้อมใช้งาน FCM แล้ว
ขั้นตอนการ Migrate จาก GCM ไปเป็น FCM (ฝั่ง Web Server)
สำหรับฝั่ง Web Server แทบจะไม่ต้องทำอะไรนอกจากเปลี่ยน Endpoint
ถ้าคุณใช้ HTTP แบบที่เคยใช้ใน GCM (Legacy)
จากเดิม
https://gcm-http.googleapis.com/gcm/send
ก็ให้เปลี่ยนเป็น
https://fcm.googleapis.com/fcm/send
ส่วนรูปแบบของข้อมูลก็เหมือนเดิมเป๊ะๆ และอย่าลืมอัปเดต Web API Key เป็นตัวใหม่ที่ได้มาจาก Firebase Console ด้วยนะ
ซึ่งวิธีการส่งข้อมูลผ่าน HTTP แบบที่เคยทำใน GCM นั้น ใน FCM จะเรียกว่า Legacy HTTP แต่ถ้าคุณอยากลองของใหม่บน FCM ก็ลองเปลี่ยนไปใช้ HTTP v1 API ดูก็ได้นะ เผื่อจะชอบ
เสร็จแล้ว ยินดีต้อนรับสู่ FCM
เห็นมั้ยล่ะ ย้ายจาก GCM ไปใช้ FCM ง่ายนิดเดียว แถมโค้ดก็น้อยกว่า แต่ฟีเจอร์ทำอะไรได้เยอะกว่า เพราะ FCM สามารถทำงานร่วมกับบริการอื่นๆใน Firebase ได้ด้วย ซึ่งต่างจาก GCM ที่ทำจะเอาไปทำงานร่วมกับอะไรก็ไม่ได้เลย ต้องมานั่ง Implement เพิ่มเองทั้งหมด