Tuỳ thuộc vào trạng thái của thiết bị, các tin nhắn đến sẽ được xử lý khác nhau. Người nhận hiểu các tình huống này và cách tích hợp FCM vào ứng dụng của riêng bạn, là điều quan trọng đầu tiên để thiết lập các trạng thái khác nhau của một thiết bị:
Tiểu bang | Mô tả |
---|---|
Nền trước | Khi ứng dụng đang mở, ở chế độ xem và đang được sử dụng. |
Thông tin cơ bản | Khi ứng dụng đang mở nhưng chạy trong nền (bị thu nhỏ). Điều này thường xảy ra khi người dùng nhấn vào "trang chủ" nút trên thiết bị, đã chuyển sang một ứng dụng khác bằng trình chuyển đổi ứng dụng, hoặc mở ứng dụng trong một tab khác (web). |
Đã chấm dứt | Khi thiết bị bị khoá hoặc ứng dụng hiện không chạy. |
Bạn cần đáp ứng một số điều kiện tiên quyết để có thể đăng ký nhận tải trọng tin nhắn qua FCM:
- Ứng dụng phải đã mở ít nhất một lần (để cho phép đăng ký với FCM).
- Trên iOS, nếu người dùng vuốt ứng dụng khỏi trình chuyển đổi ứng dụng, thì bạn phải mở lại ứng dụng theo cách thủ công để thông báo trong nền tiếp tục hoạt động.
- Trên Android, nếu người dùng buộc thoát khỏi ứng dụng trong phần cài đặt thiết bị, thì bạn phải mở lại ứng dụng theo cách thủ công để thông báo bắt đầu hoạt động.
- Trên web, bạn phải yêu cầu một mã thông báo (sử dụng
getToken()
) kèm theo chứng chỉ về thông báo đẩy trên web.
Yêu cầu cấp quyền để nhận tin nhắn
Trên iOS, macOS, web và Android 13 (trở lên), trước khi các tải trọng FCM có thể được nhận được trên thiết bị của mình, trước tiên bạn phải hỏi sự cho phép của người dùng.
Gói firebase_messaging
cung cấp một API đơn giản để yêu cầu quyền thông qua phương thức requestPermission
.
API này chấp nhận một số đối số được đặt tên xác định loại quyền mà bạn muốn yêu cầu, chẳng hạn như
tin nhắn chứa tải trọng thông báo có thể kích hoạt âm thanh hoặc đọc to tin nhắn qua Siri. Theo mặc định,
phương thức yêu cầu các quyền mặc định hợp lý. API tham chiếu cung cấp tài liệu đầy đủ về chức năng của từng quyền.
Để bắt đầu, hãy gọi phương thức từ ứng dụng của bạn (trên iOS, một phương thức gốc sẽ được hiển thị trên web luồng API gốc của trình duyệt sẽ được kích hoạt):
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}');
Thuộc tính authorizationStatus
của đối tượng NotificationSettings
được trả về từ
yêu cầu có thể được dùng để xác định quyết định tổng thể của người dùng:
authorized
: Người dùng đã cấp quyền.denied
: Người dùng đã từ chối cấp quyền.notDetermined
: Người dùng chưa chọn có cấp quyền hay không.provisional
: Người dùng đã cấp quyền tạm thời
Các thuộc tính khác trên NotificationSettings
trả về cho biết một quyền cụ thể được bật, tắt hay không được hỗ trợ trên
thiết bị.
Sau khi cấp quyền và hiểu được các loại trạng thái khác nhau của thiết bị, ứng dụng của bạn bây giờ có thể bắt đầu xử lý yêu cầu Tải trọng FCM.
Xử lý tin nhắn
Dựa trên trạng thái hiện tại của ứng dụng, các tải trọng sắp tới của loại thông báo yêu cầu các phương thức triển khai khác nhau để xử lý:
Thông báo trên nền trước
Để xử lý thông báo khi ứng dụng chạy trên nền trước, hãy nghe luồng 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}');
}
});
Luồng chứa RemoteMessage
, trình bày chi tiết
nhiều thông tin khác nhau về tải trọng, chẳng hạn như nguồn gốc của trọng tải, mã nhận dạng duy nhất, thời gian gửi, cho dù
một thông báo và nhiều thao tác khác. Vì thông báo được truy xuất trong khi ứng dụng của bạn đang chạy trên nền trước nên bạn có thể truy cập trực tiếp vào Flutter của mình
trạng thái và ngữ cảnh của ứng dụng.
Thông báo và thông báo trên nền trước
Theo mặc định, các tin nhắn thông báo xuất hiện khi ứng dụng đang chạy trên nền trước sẽ không hiện thông báo có thể nhìn thấy, trên cả Android và iOS. Tuy nhiên, bạn có thể ghi đè hành vi này:
- Trên Android, bạn phải tạo một "Mức độ ưu tiên cao" kênh thông báo.
- Trên iOS, bạn có thể cập nhật các tuỳ chọn trình bày cho ứng dụng.
Thông báo trong nền
Quy trình xử lý thông báo nền khác nhau trên ứng dụng gốc (Android và Apple) và các nền tảng dựa trên nền tảng web.
Các nền tảng của Apple và Android
Xử lý thông báo trong nền bằng cách đăng ký trình xử lý onBackgroundMessage
. Khi người dùng nhận được tin nhắn,
vùng cách ly được tạo (chỉ dành cho Android, iOS/macOS không yêu cầu phải có một vùng cách ly riêng) cho phép bạn xử lý thư ngay cả khi ứng dụng của bạn hiện không chạy.
Có một vài điều cần lưu ý về trình xử lý thông báo nền:
- Không được là chức năng ẩn danh.
- Đây phải là một hàm cấp cao nhất (ví dụ: không phải là phương thức lớp yêu cầu khởi chạy).
- Khi sử dụng Flutter phiên bản 3.3.0 trở lên, trình xử lý thông báo phải được chú thích bằng
@pragma('vm:entry-point')
ngay phía trên phần khai báo hàm (nếu không thì trình xử lý thông báo có thể bị xoá trong quá trình lắc cây đối với chế độ phát hành).
@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());
}
Vì trình xử lý chạy riêng biệt bên ngoài ngữ cảnh ứng dụng của bạn nên không thể cập nhật trạng thái ứng dụng hoặc thực thi bất kỳ logic ảnh hưởng đến giao diện người dùng nào. Tuy nhiên, bạn có thể thực hiện logic như yêu cầu HTTP, thực hiện các thao tác IO (ví dụ: cập nhật bộ nhớ cục bộ), giao tiếp với các plugin khác, v.v.
Bạn cũng nên hoàn thành logic càng sớm càng tốt. Việc chạy các tác vụ mất nhiều thời gian và công sức sẽ ảnh hưởng đến hiệu suất của thiết bị và có thể khiến hệ điều hành chấm dứt quá trình. Nếu các tác vụ chạy lâu hơn 30 giây, thì thiết bị có thể tự động dừng quy trình này.
Web
Trên web, hãy viết Service Worker của JavaScript chạy ở chế độ nền. Sử dụng trình chạy dịch vụ để xử lý các thông báo trong nền.
Để bắt đầu, hãy tạo một tệp mới trong thư mục web
và gọi tệp đó là firebase-messaging-sw.js
:
// Please see this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/master/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
Tệp này phải nhập cả SDK ứng dụng và SDK nhắn tin, khởi chạy Firebase và hiển thị biến messaging
.
Tiếp theo, bạn phải đăng ký worker này. Trong tệp index.html
, hãy đăng ký worker bằng cách sửa đổi thẻ <script>
để khởi động Flutter:
<script src="flutter_bootstrap.js" async>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
});
}
</script>
Nếu vẫn đang dùng hệ thống tạo mẫu cũ, bạn có thể đăng ký worker bằng cách sửa đổi thẻ <script>
để khởi động Flutter như sau:
<html>
<body>
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Register Firebase Messaging service worker.
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
// 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;
navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.'
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
Tiếp theo, hãy khởi động lại ứng dụng Flutter. Worker này sẽ được đăng ký và mọi thông báo trong nền sẽ được xử lý thông qua tệp này.
Xử lý tương tác
Vì thông báo là tín hiệu hiển thị nên người dùng thường tương tác với thông báo (bằng cách nhấn). Hành vi mặc định trên cả Android và iOS là mở . Nếu ứng dụng bị chấm dứt, ứng dụng sẽ được khởi động; nếu nó ở chế độ nền thì nó sẽ được đưa lên nền trước.
Tuỳ thuộc vào nội dung thông báo, bạn có thể muốn xử lý tương tác của người dùng khi ứng dụng mở. Ví dụ: nếu tin nhắn trò chuyện mới được gửi qua một thông báo và người dùng nhấn vào thông báo đó, bạn có thể muốn mở cuộc trò chuyện cụ thể khi ứng dụng mở ra.
Gói firebase-messaging
cung cấp 2 cách để xử lý hoạt động tương tác này:
getInitialMessage()
: Nếu ứng dụng được mở từ trạng thái kết thúc, thìFuture
chứaRemoteMessage
sẽ được trả về. Sau khi sử dụng,RemoteMessage
sẽ bị xoá.onMessageOpenedApp
: MộtStream
đăngRemoteMessage
khi mở ứng dụng ở trạng thái nền.
Bạn nên xử lý cả hai trường hợp này để đảm bảo trải nghiệm người dùng mượt mà. Ví dụ về mã dưới đây trình bày cách đạt được điều này:
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("...");
}
}
Cách bạn xử lý hoạt động tương tác phụ thuộc vào cách thiết lập ứng dụng của bạn. Ví dụ trên cho thấy hình minh hoạ cơ bản bằng cách sử dụng StatefulWidget.
Bản địa hoá thông báo
Bạn có thể gửi các chuỗi đã bản địa hoá theo hai cách khác nhau:
- Lưu trữ ngôn ngữ ưu tiên của từng người dùng trong máy chủ của bạn và gửi thông báo tuỳ chỉnh cho từng ngôn ngữ
- Nhúng các chuỗi đã bản địa hoá vào ứng dụng của bạn và tận dụng chế độ cài đặt ngôn ngữ bản địa của hệ điều hành
Dưới đây là cách sử dụng phương thức thứ hai:
Android
Chỉ định thông báo bằng ngôn ngữ mặc định của bạn trong
resources/values/strings.xml
:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
Chỉ định các thông báo đã dịch trong thư mục
values-language
. Ví dụ: chỉ định các thông điệp bằng tiếng Pháp trongresources/values-fr/strings.xml
:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
Trong tải trọng máy chủ, thay vì dùng khoá
title
,message
vàbody
, hãy dùngtitle_loc_key
vàbody_loc_key
cho thông báo đã bản địa hoá rồi đặt các khoá này thành thuộc tínhname
của thông báo mà bạn muốn hiển thị.Tải trọng tin nhắn sẽ có dạng như sau:
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" } }
iOS
Chỉ định thông báo bằng ngôn ngữ mặc định của bạn trong
Base.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
Chỉ định các thông báo đã dịch trong thư mục
language.lproj
. Ví dụ: chỉ định các thông điệp bằng tiếng Pháp trongfr.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
Tải trọng tin nhắn sẽ có dạng như sau:
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" } }
Bật tính năng xuất dữ liệu gửi thư
Bạn có thể xuất dữ liệu tin nhắn vào BigQuery để phân tích thêm. BigQuery cho phép bạn phân tích dữ liệu bằng BigQuery SQL, xuất dữ liệu đó sang một nhà cung cấp dịch vụ đám mây khác hoặc sử dụng dữ liệu đó cho các mô hình học máy tuỳ chỉnh. Xuất sang BigQuery bao gồm tất cả dữ liệu có sẵn cho tin nhắn, bất kể loại thông báo hoặc việc tin nhắn được gửi qua API hoặc trình soạn Thông báo.
Để bật tính năng xuất dữ liệu, trước tiên, hãy làm theo các bước mô tả ở đây, thì hãy làm theo các hướng dẫn sau:
Android
Bạn có thể dùng đoạn mã sau:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
Đối với iOS, bạn cần thay đổi AppDelegate.m
với nội dung sau.
#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
Web
Đối với Web, bạn cần thay đổi trình chạy dịch vụ của mình để sử dụng phiên bản SDK v9.
Phiên bản v9 cần được đi kèm, vì vậy, bạn cần sử dụng một trình gói như esbuild
chẳng hạn
để nhân viên dịch vụ làm việc.
Hãy xem ứng dụng ví dụ để biết cách thực hiện việc này.
Sau khi di chuyển sang SDK phiên bản 9, bạn có thể sử dụng mã sau:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Đừng quên chạy yarn build
để xuất phiên bản mới của trình chạy dịch vụ sang thư mục web
.
Hiển thị hình ảnh trong thông báo trên iOS
Trên các thiết bị của Apple, để Thông báo FCM đến hiển thị hình ảnh từ tải trọng FCM, bạn phải thêm tiện ích dịch vụ thông báo bổ sung và định cấu hình ứng dụng của mình để sử dụng tiện ích đó.
Nếu đang sử dụng phương thức xác thực điện thoại Firebase, bạn phải thêm Nhóm xác thực Firebase vào Podfile của mình.
Bước 1 – Thêm tiện ích dịch vụ thông báo
- Trong Xcode, hãy nhấp vào Tệp > Mới > Mục tiêu...
- Một cửa sổ phụ sẽ hiển thị danh sách các mục tiêu khả thi; cuộn xuống hoặc sử dụng bộ lọc để chọn Tiện ích dịch vụ thông báo. Nhấp vào Tiếp theo.
- Thêm tên sản phẩm (sử dụng "ImageNotification" ("Thông báo hình ảnh") để làm theo hướng dẫn này), đặt ngôn ngữ thành Target-C và nhấp vào Finish (Hoàn tất).
- Bật lược đồ bằng cách nhấp vào Kích hoạt.
Bước 2 – Thêm mục tiêu vào Podfile
Hãy đảm bảo rằng tiện ích mới của bạn có quyền truy cập vào nhóm Firebase/Messaging
bằng cách thêm tiện ích đó vào Podfile:
Trên Trình điều hướng, hãy mở Podfile: Pods > Tệp nhóm
Di chuyển xuống cuối tệp rồi thêm:
target 'ImageNotification' do use_frameworks! pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication pod 'Firebase/Messaging' end
Cài đặt hoặc cập nhật các nhóm của bạn bằng cách sử dụng
pod install
từ thư mụcios
hoặcmacos
.
Bước 3 – Sử dụng trình trợ giúp tiện ích
Tại thời điểm này, mọi thứ vẫn sẽ chạy bình thường. Bước cuối cùng là gọi trình trợ giúp tiện ích.
Từ trình điều hướng, hãy chọn tiện ích ImageNotification của bạn
Mở tệp
NotificationService.m
.Ở đầu tệp, hãy nhập
FirebaseMessaging.h
ngay sauNotificationService.h
như minh hoạ dưới đây.Thay thế nội dung của
NotificationService.m
bằng:#import "NotificationService.h" #import "FirebaseMessaging.h" #import "FirebaseAuth.h" // Add this line if you are using FirebaseAuth phone authentication #import <UIKit/UIKit.h> // Add this line if you are using FirebaseAuth phone authentication @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService /* Uncomment this if you are using Firebase Auth - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { if ([[FIRAuth auth] canHandleURL:url]) { return YES; } return NO; } - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { for (UIOpenURLContext *urlContext in URLContexts) { [FIRAuth.auth canHandleURL:urlContext.URL]; } } */ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // Modify the notification content here... [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end
Bước 4 – Thêm hình ảnh vào tải trọng
Trong tải trọng thông báo, giờ đây bạn có thể thêm hình ảnh. Hãy xem tài liệu dành cho iOS về cách tạo một yêu cầu gửi. Xin lưu ý rằng thiết bị phải thực thi kích thước hình ảnh tối đa là 300KB.