Dựa trên mô hình phát hành/đăng ký, tính năng nhắn tin theo chủ đề FCM cho phép bạn gửi tin nhắn đến nhiều thiết bị đã chọn sử dụng một chủ đề cụ thể. Bạn soạn thông báo theo chủ đề khi cần và FCM sẽ xử lý việc định tuyến và phân phối thông báo một cách đáng tin cậy đến đúng thiết bị.
Ví dụ: người dùng ứng dụng dự báo thủy triều tại địa phương có thể chọn sử dụng chủ đề "cảnh báo về dòng thủy triều" và nhận thông báo về điều kiện câu cá nước mặn tối ưu ở các khu vực được chỉ định. Người dùng ứng dụng thể thao có thể đăng ký nhận thông tin cập nhật tự động về tỷ số trực tiếp của các đội mà họ yêu thích.
Một số điều cần lưu ý về chủ đề:
- Thông báo theo chủ đề phù hợp nhất với nội dung như thời tiết hoặc thông tin khác có sẵn công khai.
- Tin nhắn theo chủ đề được tối ưu hoá cho băng thông thay vì độ trễ. Để phân phối nhanh chóng và an toàn đến một thiết bị hoặc một nhóm nhỏ thiết bị, hãy nhắm mục tiêu tin nhắn đến mã thông báo đăng ký, chứ không phải chủ đề.
- Nếu bạn cần gửi tin nhắn đến nhiều thiết bị cho mỗi người dùng, hãy cân nhắc việc nhắn tin theo nhóm thiết bị cho các trường hợp sử dụng đó.
- Tính năng nhắn tin theo chủ đề hỗ trợ số lượng đăng ký không giới hạn cho mỗi chủ đề. Tuy nhiên, FCM thực thi các giới hạn trong những lĩnh vực sau:
- Một thực thể ứng dụng có thể đăng ký theo dõi không quá 2.000 chủ đề.
- Nếu bạn đang sử dụng tính năng nhập hàng loạt để đăng ký các phiên bản ứng dụng, thì mỗi yêu cầu sẽ bị giới hạn ở 1.000 phiên bản ứng dụng.
- Tần suất của các gói thuê bao mới bị giới hạn tốc độ trên mỗi dự án. Nếu bạn gửi quá nhiều yêu cầu đăng ký trong một khoảng thời gian ngắn, máy chủ FCM sẽ phản hồi bằng một phản hồi
429 RESOURCE_EXHAUSTED
("quá hạn mức"). Thử lại với thời gian đợi luỹ thừa.
Đăng ký ứng dụng khách cho một chủ đề
Ứng dụng khách có thể đăng ký bất kỳ chủ đề hiện có nào hoặc tạo một chủ đề mới. Khi một ứng dụng khách đăng ký tên chủ đề mới (chưa có trong dự án Firebase của bạn), một chủ đề mới có tên đó sẽ được tạo trong FCM và sau đó, mọi ứng dụng khách đều có thể đăng ký chủ đề đó.
Để đăng ký một chủ đề, hãy gọi phương thức đăng ký từ luồng chính của ứng dụng (FCM không an toàn cho luồng). Nếu yêu cầu đăng ký ban đầu không thành công, FCM sẽ tự động thử lại. Trong trường hợp không thể hoàn tất gói thuê bao, gói thuê bao sẽ gửi một lỗi mà bạn có thể phát hiện trong trình xử lý hoàn tất như sau:
Swift
Messaging.messaging().subscribe(toTopic: "weather") { error in print("Subscribed to weather topic") }
Objective-C
[[FIRMessaging messaging] subscribeToTopic:@"weather" completion:^(NSError * _Nullable error) { NSLog(@"Subscribed to weather topic"); }];
Lệnh gọi này thực hiện một yêu cầu không đồng bộ đến phần phụ trợ FCM và đăng ký ứng dụng khách theo chủ đề đã cho. Trước khi gọi subscribeToTopic:topic
, hãy đảm bảo rằng thực thể ứng dụng khách đã nhận được mã thông báo đăng ký thông qua lệnh gọi lại didReceiveRegistrationToken
.
Mỗi khi ứng dụng khởi động, FCM sẽ đảm bảo rằng tất cả các chủ đề được yêu cầu đều đã được đăng ký. Để huỷ đăng ký, hãy gọi unsubscribeFromTopic:topic
và FCM sẽ huỷ đăng ký chủ đề trong nền.
Quản lý gói thuê bao chủ đề trên máy chủ
Firebase Admin SDK cho phép bạn thực hiện các thao tác quản lý chủ đề cơ bản từ phía máy chủ. Với(các) mã thông báo đăng ký, bạn có thể đăng ký và huỷ đăng ký hàng loạt các thực thể ứng dụng khách bằng cách sử dụng logic máy chủ.
Bạn có thể đăng ký các thực thể ứng dụng khách cho bất kỳ chủ đề hiện có nào hoặc tạo một chủ đề mới. Khi bạn sử dụng API để đăng ký một ứng dụng khách cho một chủ đề mới (chưa tồn tại cho dự án Firebase của bạn), một chủ đề mới có tên đó sẽ được tạo trong FCM và sau đó, mọi ứng dụng khách đều có thể đăng ký chủ đề đó.
Bạn có thể truyền danh sách mã thông báo đăng ký đến phương thức đăng ký Firebase Admin SDK để đăng ký các thiết bị tương ứng với một chủ đề:
Node.js
// These registration tokens come from the client FCM SDKs.
const registrationTokens = [
'YOUR_REGISTRATION_TOKEN_1',
// ...
'YOUR_REGISTRATION_TOKEN_n'
];
// Subscribe the devices corresponding to the registration tokens to the
// topic.
getMessaging().subscribeToTopic(registrationTokens, topic)
.then((response) => {
// See the MessagingTopicManagementResponse reference documentation
// for the contents of response.
console.log('Successfully subscribed to topic:', response);
})
.catch((error) => {
console.log('Error subscribing to topic:', error);
});
Java
// These registration tokens come from the client FCM SDKs.
List<String> registrationTokens = Arrays.asList(
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n"
);
// Subscribe the devices corresponding to the registration tokens to the
// topic.
TopicManagementResponse response = FirebaseMessaging.getInstance().subscribeToTopic(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " tokens were subscribed successfully");
Python
# These registration tokens come from the client FCM SDKs.
registration_tokens = [
'YOUR_REGISTRATION_TOKEN_1',
# ...
'YOUR_REGISTRATION_TOKEN_n',
]
# Subscribe the devices corresponding to the registration tokens to the
# topic.
response = messaging.subscribe_to_topic(registration_tokens, topic)
# See the TopicManagementResponse reference documentation
# for the contents of response.
print(response.success_count, 'tokens were subscribed successfully')
Go
// These registration tokens come from the client FCM SDKs.
registrationTokens := []string{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
}
// Subscribe the devices corresponding to the registration tokens to the
// topic.
response, err := client.SubscribeToTopic(ctx, registrationTokens, topic)
if err != nil {
log.Fatalln(err)
}
// See the TopicManagementResponse reference documentation
// for the contents of response.
fmt.Println(response.SuccessCount, "tokens were subscribed successfully")
C#
// These registration tokens come from the client FCM SDKs.
var registrationTokens = new List<string>()
{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
};
// Subscribe the devices corresponding to the registration tokens to the
// topic
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
Console.WriteLine($"{response.SuccessCount} tokens were subscribed successfully");
API FCM dành cho quản trị viên cũng cho phép bạn huỷ đăng ký thiết bị khỏi một chủ đề bằng cách truyền mã thông báo đăng ký đến phương thức thích hợp:
Node.js
// These registration tokens come from the client FCM SDKs.
const registrationTokens = [
'YOUR_REGISTRATION_TOKEN_1',
// ...
'YOUR_REGISTRATION_TOKEN_n'
];
// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
getMessaging().unsubscribeFromTopic(registrationTokens, topic)
.then((response) => {
// See the MessagingTopicManagementResponse reference documentation
// for the contents of response.
console.log('Successfully unsubscribed from topic:', response);
})
.catch((error) => {
console.log('Error unsubscribing from topic:', error);
});
Java
// These registration tokens come from the client FCM SDKs.
List<String> registrationTokens = Arrays.asList(
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n"
);
// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
TopicManagementResponse response = FirebaseMessaging.getInstance().unsubscribeFromTopic(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " tokens were unsubscribed successfully");
Python
# These registration tokens come from the client FCM SDKs.
registration_tokens = [
'YOUR_REGISTRATION_TOKEN_1',
# ...
'YOUR_REGISTRATION_TOKEN_n',
]
# Unubscribe the devices corresponding to the registration tokens from the
# topic.
response = messaging.unsubscribe_from_topic(registration_tokens, topic)
# See the TopicManagementResponse reference documentation
# for the contents of response.
print(response.success_count, 'tokens were unsubscribed successfully')
Go
// These registration tokens come from the client FCM SDKs.
registrationTokens := []string{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
}
// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
response, err := client.UnsubscribeFromTopic(ctx, registrationTokens, topic)
if err != nil {
log.Fatalln(err)
}
// See the TopicManagementResponse reference documentation
// for the contents of response.
fmt.Println(response.SuccessCount, "tokens were unsubscribed successfully")
C#
// These registration tokens come from the client FCM SDKs.
var registrationTokens = new List<string>()
{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
};
// Unsubscribe the devices corresponding to the registration tokens from the
// topic
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
Console.WriteLine($"{response.SuccessCount} tokens were unsubscribed successfully");
Phương thức subscribeToTopic()
và unsubscribeFromTopic()
sẽ tạo ra một đối tượng chứa phản hồi từ FCM. Loại dữ liệu trả về có cùng định dạng bất kể số lượng mã thông báo đăng ký được chỉ định trong yêu cầu.
Trong trường hợp xảy ra lỗi (lỗi xác thực, mã thông báo hoặc chủ đề không hợp lệ, v.v.), các phương thức này sẽ dẫn đến lỗi. Để biết danh sách đầy đủ các mã lỗi, bao gồm cả nội dung mô tả và các bước khắc phục, hãy xem phần Lỗi API FCM của quản trị viên.
Nhận và xử lý tin nhắn theo chủ đề
FCM phân phối thông báo theo chủ đề giống như các thông báo hạ nguồn khác.
Triển khai application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
như sau:
Swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async -> UIBackgroundFetchResult { // If you are receiving a notification message while your app is in the background, // this callback will not be fired till the user taps on the notification launching the application. // TODO: Handle data of notification // With swizzling disabled you must let Messaging know about the message, for Analytics // Messaging.messaging().appDidReceiveMessage(userInfo) // Print message ID. if let messageID = userInfo[gcmMessageIDKey] { print("Message ID: \(messageID)") } // Print full message. print(userInfo) return UIBackgroundFetchResult.newData }
Objective-C
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // If you are receiving a notification message while your app is in the background, // this callback will not be fired till the user taps on the notification launching the application. // TODO: Handle data of notification // With swizzling disabled you must let Messaging know about the message, for Analytics // [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; // ... // Print full message. NSLog(@"%@", userInfo); completionHandler(UIBackgroundFetchResultNewData); }
Tạo yêu cầu gửi
Sau khi tạo một chủ đề, bạn có thể gửi thông báo đến chủ đề đó bằng cách đăng ký các thực thể ứng dụng khách cho chủ đề ở phía máy khách hoặc thông qua API máy chủ. Nếu đây là lần đầu tiên bạn tạo yêu cầu gửi cho FCM, hãy xem hướng dẫn về môi trường máy chủ và FCM để biết thông tin quan trọng về nền tảng và cách thiết lập.
Trong logic gửi ở phần phụ trợ, hãy chỉ định tên chủ đề mong muốn như sau:
Node.js
// The topic name can be optionally prefixed with "/topics/".
const topic = 'highScores';
const message = {
data: {
score: '850',
time: '2:45'
},
topic: topic
};
// Send a message to devices subscribed to the provided topic.
getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
Java
// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";
// See documentation on defining a message payload.
Message message = Message.builder()
.putData("score", "850")
.putData("time", "2:45")
.setTopic(topic)
.build();
// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);
Python
# The topic name can be optionally prefixed with "/topics/".
topic = 'highScores'
# See documentation on defining a message payload.
message = messaging.Message(
data={
'score': '850',
'time': '2:45',
},
topic=topic,
)
# Send a message to the devices subscribed to the provided topic.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)
Go
// The topic name can be optionally prefixed with "/topics/".
topic := "highScores"
// See documentation on defining a message payload.
message := &messaging.Message{
Data: map[string]string{
"score": "850",
"time": "2:45",
},
Topic: topic,
}
// Send a message to the devices subscribed to the provided topic.
response, err := client.Send(ctx, message)
if err != nil {
log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)
C#
// The topic name can be optionally prefixed with "/topics/".
var topic = "highScores";
// See documentation on defining a message payload.
var message = new Message()
{
Data = new Dictionary<string, string>()
{
{ "score", "850" },
{ "time", "2:45" },
},
Topic = topic,
};
// Send a message to the devices subscribed to the provided topic.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);
Kiến trúc chuyển trạng thái đại diện (REST)
POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
"message":{
"topic" : "foo-bar",
"notification" : {
"body" : "This is a Firebase Cloud Messaging Topic Message!",
"title" : "FCM Message"
}
}
}
Lệnh cURL:
curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
"message": {
"topic" : "foo-bar",
"notification": {
"body": "This is a Firebase Cloud Messaging Topic Message!",
"title": "FCM Message"
}
}
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Để gửi thông báo đến một kết hợp các chủ đề, hãy chỉ định một điều kiện. Đây là một biểu thức boolean chỉ định các chủ đề mục tiêu. Ví dụ: điều kiện sau đây sẽ gửi thông báo đến các thiết bị đăng ký TopicA
và TopicB
hoặc TopicC
:
"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"
Trước tiên, FCM sẽ đánh giá mọi điều kiện trong dấu ngoặc đơn, sau đó đánh giá biểu thức từ trái sang phải. Trong biểu thức trên, người dùng đăng ký một chủ đề bất kỳ sẽ không nhận được thông báo. Tương tự, người dùng không đăng ký TopicA
sẽ không nhận được thông báo. Các tổ hợp sau đây sẽ nhận được thông báo:
TopicA
vàTopicB
TopicA
vàTopicC
Bạn có thể đưa tối đa 5 chủ đề vào biểu thức có điều kiện.
Cách gửi đến một điều kiện:
Node.js
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
const condition = '\'stock-GOOG\' in topics || \'industry-tech\' in topics';
// See documentation on defining a message payload.
const message = {
notification: {
title: '$FooCorp up 1.43% on the day',
body: '$FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'
},
condition: condition
};
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
Java
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
String condition = "'stock-GOOG' in topics || 'industry-tech' in topics";
// See documentation on defining a message payload.
Message message = Message.builder()
.setNotification(Notification.builder()
.setTitle("$GOOG up 1.43% on the day")
.setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.")
.build())
.setCondition(condition)
.build();
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);
Python
# Define a condition which will send to devices which are subscribed
# to either the Google stock or the tech industry topics.
condition = "'stock-GOOG' in topics || 'industry-tech' in topics"
# See documentation on defining a message payload.
message = messaging.Message(
notification=messaging.Notification(
title='$GOOG up 1.43% on the day',
body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
),
condition=condition,
)
# Send a message to devices subscribed to the combination of topics
# specified by the provided condition.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)
Go
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
condition := "'stock-GOOG' in topics || 'industry-tech' in topics"
// See documentation on defining a message payload.
message := &messaging.Message{
Data: map[string]string{
"score": "850",
"time": "2:45",
},
Condition: condition,
}
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
response, err := client.Send(ctx, message)
if err != nil {
log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)
C#
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";
// See documentation on defining a message payload.
var message = new Message()
{
Notification = new Notification()
{
Title = "$GOOG up 1.43% on the day",
Body = "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
},
Condition = condition,
};
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);
Kiến trúc chuyển trạng thái đại diện (REST)
POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
"message":{
"condition": "'dogs' in topics || 'cats' in topics",
"notification" : {
"body" : "This is a Firebase Cloud Messaging Topic Message!",
"title" : "FCM Message",
}
}
}
Lệnh cURL:
curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
"notification": {
"title": "FCM Message",
"body": "This is a Firebase Cloud Messaging Topic Message!",
},
"condition": "'dogs' in topics || 'cats' in topics"
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Các bước tiếp theo
- Tìm hiểu thêm về cách khác để gửi đến nhiều thiết bị – Nhắn tin theo nhóm thiết bị