ข้อความขาเข้าจะได้รับการจัดการแตกต่างกันไปขึ้นอยู่กับสถานะของอุปกรณ์ เพื่อให้เข้าใจสถานการณ์เหล่านี้และวิธีรวม FCM เข้ากับแอปพลิเคชันของคุณเอง สิ่งสำคัญอันดับแรกคือต้องกำหนดสถานะต่างๆ ที่อุปกรณ์สามารถเป็นได้:
สถานะ | คำอธิบาย |
---|---|
เบื้องหน้า | เมื่อแอปพลิเคชันเปิดอยู่ในมุมมองและใช้งานอยู่ |
พื้นหลัง | เมื่อแอปพลิเคชันเปิดอยู่ แต่อยู่ในพื้นหลัง (ย่อเล็กสุด) กรณีนี้มักเกิดขึ้นเมื่อผู้ใช้กดปุ่ม "หน้าแรก" บนอุปกรณ์ เปลี่ยนไปใช้แอปอื่นโดยใช้ตัวสลับแอป หรือเปิดแอปพลิเคชันในแท็บอื่น (เว็บ) |
สิ้นสุด | เมื่ออุปกรณ์ถูกล็อกหรือแอปพลิเคชันไม่ทำงาน |
มีเงื่อนไขเบื้องต้นบางประการที่ต้องปฏิบัติตามก่อนที่แอปพลิเคชันจะสามารถรับเพย์โหลดข้อความผ่าน FCM:
- ต้องเปิดแอปพลิเคชันอย่างน้อยหนึ่งครั้ง (เพื่อให้สามารถลงทะเบียนกับ FCM)
- บน iOS หากผู้ใช้ปัดแอปพลิเคชันออกจากตัวสลับแอป จะต้องเปิดแอปนั้นใหม่ด้วยตนเองเพื่อให้ข้อความพื้นหลังเริ่มทำงานอีกครั้ง
- ใน Android หากผู้ใช้บังคับปิดแอปจากการตั้งค่าอุปกรณ์ จะต้องเปิดแอปอีกครั้งด้วยตนเองเพื่อให้ข้อความเริ่มทำงาน
- บนเว็บ คุณต้องขอโทเค็น (โดยใช้
getToken()
) กับใบรับรองการพุชเว็บของคุณ
ขออนุญาตรับข้อความ (Apple และ Web)
บน iOS, macOS และเว็บ ก่อนที่จะสามารถรับเพย์โหลด FCM บนอุปกรณ์ของคุณได้ คุณต้องขออนุญาตจากผู้ใช้ก่อน
แพ็คเกจ firebase_messaging
มี API อย่างง่ายสำหรับการขออนุญาตผ่านเมธอด requestPermission
API นี้ยอมรับอาร์กิวเมนต์ที่มีชื่อจำนวนหนึ่งซึ่งกำหนดประเภทของการอนุญาตที่คุณต้องการขอ เช่น การส่งข้อความที่มีเพย์โหลดการแจ้งเตือนสามารถเรียกเสียงหรืออ่านข้อความผ่าน Siri ได้หรือไม่ โดยค่าเริ่มต้น เมธอดจะร้องขอสิทธิ์เริ่มต้นที่เหมาะสม API อ้างอิงมีเอกสารฉบับเต็มเกี่ยวกับการอนุญาตแต่ละรายการ
ในการเริ่มต้น ให้เรียกใช้เมธอดจากแอปพลิเคชันของคุณ (บน iOS จะแสดงโมดอลแบบเนทีฟ และบนเว็บ โฟลว์ API แบบเนทีฟของเบราว์เซอร์จะถูกทริกเกอร์):
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
สามารถใช้คุณสมบัติ authorizationStatus
ของอ็อบเจกต์ NotificationSettings
ที่ส่งกลับจากคำขอเพื่อพิจารณาการตัดสินใจโดยรวมของผู้ใช้:
-
authorized
: ผู้ใช้ได้รับอนุญาต -
denied
: ผู้ใช้ปฏิเสธการอนุญาต -
notDetermined
: ผู้ใช้ยังไม่ได้เลือกว่าจะอนุญาตหรือไม่ -
provisional
: ผู้ใช้ได้รับอนุญาตชั่วคราว
คุณสมบัติอื่น ๆ ในการตั้งค่า NotificationSettings
จะส่งคืนว่ามีการเปิดใช้งาน ปิดใช้งาน หรือไม่รองรับการอนุญาตเฉพาะในอุปกรณ์ปัจจุบัน
เมื่อได้รับอนุญาตและเข้าใจสถานะอุปกรณ์ประเภทต่างๆ แล้ว แอปพลิเคชันของคุณสามารถเริ่มจัดการกับเพย์โหลด FCM ที่เข้ามาได้แล้ว
การจัดการข้อความ
ขึ้นอยู่กับสถานะปัจจุบันของแอปพลิเคชันของคุณ เพย์โหลดขาเข้าของ ข้อความประเภทต่างๆ จำเป็นต้องมีการใช้งานที่แตกต่างกันเพื่อจัดการ:
ข้อความเบื้องหน้า
หากต้องการจัดการข้อความขณะที่แอปพลิเคชันของคุณอยู่เบื้องหน้า ให้ฟังสตรีม onMessage
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
สตรีมประกอบด้วย RemoteMessage
ซึ่งให้รายละเอียดข้อมูลต่างๆ เกี่ยวกับเพย์โหลด เช่น แหล่งที่มา ID เฉพาะ เวลาที่ส่ง มีการแจ้งเตือนหรือไม่ และอื่นๆ เนื่องจากมีการดึงข้อความในขณะที่แอปพลิเคชันของคุณอยู่เบื้องหน้า คุณจึงสามารถเข้าถึงสถานะและบริบทของแอปพลิเคชัน Flutter ได้โดยตรง
ข้อความเบื้องหน้าและการแจ้งเตือน
ข้อความแจ้งเตือนที่มาถึงในขณะที่แอปพลิเคชันอยู่เบื้องหน้าจะไม่แสดงการแจ้งเตือนที่มองเห็นได้ตามค่าเริ่มต้น ทั้งบน Android และ iOS อย่างไรก็ตาม เป็นไปได้ที่จะแทนที่พฤติกรรมนี้:
- บน Android คุณต้องสร้างช่องทางการแจ้งเตือน "ลำดับความสำคัญสูง"
- บน iOS คุณสามารถอัปเดตตัวเลือกการนำเสนอสำหรับแอปพลิเคชันได้
ข้อความพื้นหลัง
กระบวนการจัดการข้อความพื้นหลังจะแตกต่างกันในแพลตฟอร์มเนทีฟ (Android และ Apple) และบนเว็บ
แพลตฟอร์ม Apple และ Android
จัดการข้อความพื้นหลังโดยลงทะเบียนตัวจัดการ onBackgroundMessage
เมื่อได้รับข้อความ การแยกจะถูกสร้างขึ้น (เฉพาะ Android, iOS/macOS ไม่ต้องการการแยกแยกต่างหาก) ช่วยให้คุณจัดการข้อความได้แม้ในขณะที่แอปพลิเคชันของคุณไม่ได้ทำงานอยู่
มีบางสิ่งที่ควรทราบเกี่ยวกับตัวจัดการข้อความพื้นหลังของคุณ:
- จะต้องไม่เป็นฟังก์ชันนิรนาม
- ต้องเป็นฟังก์ชันระดับบนสุด (เช่น ไม่ใช่เมธอดคลาสที่ต้องเตรียมใช้งาน)
- ต้องมีคำอธิบายประกอบด้วย
@pragma('vm:entry-point')
เหนือการประกาศฟังก์ชัน (มิฉะนั้นอาจถูกลบออกระหว่างการเขย่าต้นไม้สำหรับโหมดรีลีส)
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
เนื่องจากตัวจัดการทำงานในแยกของตัวเองนอกบริบทแอปพลิเคชันของคุณ จึงไม่สามารถอัปเดตสถานะแอปพลิเคชันหรือดำเนินการ UI ที่ส่งผลกระทบต่อลอจิกได้ อย่างไรก็ตาม คุณสามารถดำเนินการตรรกะ เช่น คำขอ HTTP, ดำเนินการ IO (เช่น อัปเดตที่เก็บข้อมูลในเครื่อง), สื่อสารกับปลั๊กอินอื่น ๆ เป็นต้น
ขอแนะนำให้กรอกตรรกะของคุณโดยเร็วที่สุด การทำงานที่ยาวนานและเข้มข้นส่งผลกระทบต่อประสิทธิภาพของอุปกรณ์และอาจทำให้ระบบปฏิบัติการยุติกระบวนการ หากทำงานนานกว่า 30 วินาที อุปกรณ์อาจหยุดกระบวนการโดยอัตโนมัติ
เว็บ
บนเว็บ ให้เขียน JavaScript Service Worker ซึ่งทำงานในพื้นหลัง ใช้พนักงานบริการเพื่อจัดการข้อความพื้นหลัง
ในการเริ่มต้น ให้สร้างไฟล์ใหม่ในไดเร็กทอรี web
ของคุณ และเรียกมันว่า firebase-messaging-sw.js
:
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
ไฟล์ต้องนำเข้าทั้งแอปและ SDK การส่งข้อความ เริ่มต้น Firebase และเปิดเผยตัวแปร messaging
ถัดไปจะต้องลงทะเบียนคนงาน ภายในไฟล์รายการ หลังจาก โหลดไฟล์ main.dart.js
แล้ว ให้ลงทะเบียนผู้ปฏิบัติงานของคุณ:
<html>
<body>
...
<script src="main.dart.js" type="application/javascript"></script>
<script>
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// ADD THIS LINE
navigator.serviceWorker.register('/firebase-messaging-sw.js');
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
// ...
});
}
</script>
ถัดไปรีสตาร์ทแอปพลิเคชัน Flutter ของคุณ ผู้ปฏิบัติงานจะได้รับการลงทะเบียนและข้อความเบื้องหลังจะถูกจัดการผ่านไฟล์นี้
ปฏิสัมพันธ์การจัดการ
เนื่องจากการแจ้งเตือนเป็นสิ่งที่มองเห็นได้ จึงเป็นเรื่องปกติที่ผู้ใช้จะโต้ตอบกับพวกเขา (โดยการกด) ลักษณะการทำงานเริ่มต้นทั้งบน Android และ iOS คือเปิดแอปพลิเคชัน หากแอปพลิเคชันถูกยกเลิก แอปพลิเคชันจะเริ่มทำงาน หากอยู่ในพื้นหลังก็จะถูกนำไปที่เบื้องหน้า
คุณอาจต้องการจัดการการโต้ตอบของผู้ใช้เมื่อแอปพลิเคชันเปิดขึ้น ทั้งนี้ขึ้นอยู่กับเนื้อหาของการแจ้งเตือน ตัวอย่างเช่น หากข้อความแชทใหม่ถูกส่งผ่านการแจ้งเตือนและผู้ใช้กดข้อความนั้น คุณอาจต้องการเปิดการสนทนาที่ต้องการเมื่อแอปพลิเคชันเปิดขึ้น
แพ็คเกจ firebase-messaging
มีสองวิธีในการจัดการการโต้ตอบนี้:
-
getInitialMessage()
: หากแอปพลิเคชันถูกเปิดจากสถานะสิ้นสุดFuture
ที่มีRemoteMessage
จะถูกส่งคืน เมื่อใช้แล้วRemoteMessage
จะถูกลบออก -
onMessageOpenedApp
:Stream
ที่โพสต์RemoteMessage
เมื่อเปิดแอปพลิเคชันจากสถานะเบื้องหลัง
ขอแนะนำให้จัดการทั้งสองสถานการณ์เพื่อให้แน่ใจว่า UX ที่ราบรื่นสำหรับผู้ใช้ของคุณ ตัวอย่างโค้ดด้านล่างแสดงวิธีการบรรลุผลสำเร็จ:
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
// It is assumed that all messages contain a data field with the key 'type'
Future<void> setupInteractedMessage() async {
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
_handleMessage(initialMessage);
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
}
void _handleMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
Navigator.pushNamed(context, '/chat',
arguments: ChatArguments(message),
);
}
}
@override
void initState() {
super.initState();
// Run code required to handle interacted messages in an async function
// as initState() must not be async
setupInteractedMessage();
}
@override
Widget build(BuildContext context) {
return Text("...");
}
}
วิธีจัดการกับการโต้ตอบขึ้นอยู่กับการตั้งค่าแอปพลิเคชันของคุณ ตัวอย่างด้านบนแสดงภาพประกอบพื้นฐานโดยใช้ StatefulWidget
แปลข้อความ
คุณสามารถส่งสตริงที่แปลเป็นภาษาท้องถิ่นได้สองวิธี:
- จัดเก็บภาษาที่ต้องการของผู้ใช้แต่ละคนในเซิร์ฟเวอร์ของคุณ และส่งการแจ้งเตือนที่กำหนดเองสำหรับแต่ละภาษา
- ฝังสตริงที่แปลเป็นภาษาท้องถิ่นในแอปของคุณและใช้ประโยชน์จากการตั้งค่าภาษาท้องถิ่นของระบบปฏิบัติการ
นี่คือวิธีการใช้วิธีที่สอง:
แอนดรอยด์
ระบุข้อความภาษาเริ่มต้นของคุณใน
resources/values/strings.xml
:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
ระบุข้อความที่แปลในไดเร็กทอรี
values- language
ค่า ตัวอย่างเช่น ระบุข้อความภาษาฝรั่งเศสในresources/values-fr/strings.xml
:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
ใน payload ของเซิร์ฟเวอร์ แทนที่จะใช้
title
message
และคีย์body
ให้ใช้title_loc_key
และbody_loc_key
สำหรับข้อความที่แปลเป็นภาษาท้องถิ่นของคุณ และตั้งค่าให้เป็นแอตทริบิวต์name
ของข้อความที่คุณต้องการแสดงเพย์โหลดข้อความจะมีลักษณะดังนี้:
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" }, }
iOS
ระบุข้อความภาษาเริ่มต้นของคุณใน
Base.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
ระบุข้อความที่แปลในไดเร็กทอรี
language .lproj
ตัวอย่างเช่น ระบุข้อความภาษาฝรั่งเศสในfr.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
เพย์โหลดข้อความจะมีลักษณะดังนี้:
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" }, }
เปิดใช้งานการส่งออกข้อมูลการส่งข้อความ
คุณส่งออกข้อมูลข้อความไปยัง BigQuery เพื่อวิเคราะห์เพิ่มเติมได้ BigQuery ช่วยให้คุณวิเคราะห์ข้อมูลโดยใช้ BigQuery SQL ส่งออกไปยังผู้ให้บริการระบบคลาวด์รายอื่น หรือใช้ข้อมูลสำหรับโมเดล ML ที่กำหนดเอง การส่งออกไปยัง BigQuery จะรวมข้อมูลที่มีอยู่ทั้งหมดสำหรับข้อความ โดยไม่คำนึงถึงประเภทข้อความหรือว่าข้อความนั้นส่งผ่าน API หรือตัวเขียนการแจ้งเตือนหรือไม่
หากต้องการเปิดใช้งานการส่งออก ก่อนอื่นให้ทำตามขั้นตอน ที่อธิบายไว้ที่นี่ จากนั้นทำตามคำแนะนำเหล่านี้:
แอนดรอยด์
คุณสามารถใช้โค้ดต่อไปนี้: dart await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
สำหรับ iOS คุณต้องเปลี่ยน AppDelegate.m
ด้วยเนื้อหาต่อไปนี้
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end
เว็บ
สำหรับเว็บ คุณต้องเปลี่ยนพนักงานบริการของคุณเพื่อใช้ SDK เวอร์ชัน v9 ต้องรวมเวอร์ชัน v9 ดังนั้นคุณต้องใช้ตัวรวมเช่น esbuild
เพื่อให้เจ้าหน้าที่บริการทำงาน ดู แอปตัวอย่าง เพื่อดูวิธีการบรรลุเป้าหมายนี้
เมื่อคุณย้ายไปที่ v9 SDK แล้ว คุณสามารถใช้รหัสต่อไปนี้:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
อย่าลืมรัน yarn build
เพื่อส่งออก Service Worker เวอร์ชันใหม่ไปยังโฟลเดอร์ web