基於發布/訂閱模型,FCM 主題訊息傳遞可讓您向選擇加入特定主題的多個裝置發送訊息。您根據需要撰寫主題訊息,FCM 負責路由並將訊息可靠地傳遞到正確的裝置。
例如,本地潮汐預報應用程式的使用者可以選擇「潮流警報」主題,並接收指定區域最佳鹹水捕魚條件的通知。體育應用程式的用戶可以訂閱他們最喜歡的球隊的即時比賽比分的自動更新。
關於主題需要記住的一些事項:
- 主題訊息最適合天氣或其他公開資訊等內容。
- 主題訊息針對吞吐量而不是延遲進行了最佳化。為了快速、安全地傳遞到單一設備或小組設備,請將訊息定位到註冊令牌,而不是主題。
- 如果您需要向每個使用者的多個裝置發送訊息,請考慮針對這些用例的裝置群組訊息傳遞。
- 主題訊息支持每個主題的無限訂閱。然而,FCM 在這些領域實施限制:
- 一個應用實例最多可以訂閱2000個主題。
- 如果您使用批次匯入來訂閱應用程式實例,則每個請求僅限於 1000 個應用程式實例。
- 每個項目的新訂閱頻率受到速率限制。如果您在短時間內發送太多訂閱請求,FCM 伺服器將回應
429 RESOURCE_EXHAUSTED
(「超出配額」)回應。使用指數退避重試。
為客戶端應用程式訂閱主題
客戶端應用程式可以訂閱任何現有主題,也可以建立新主題。當用戶端套用訂閱新的主題名稱(您的 Firebase 專案尚不存在的主題)時,會在 FCM 中建立具有該名稱的新主題,並且隨後任何客戶端都可以訂閱它。
要訂閱主題,請從應用程式的主執行緒呼叫訂閱方法(FCM 不是線程安全的)。如果訂閱請求最初失敗,FCM 會自動重試。對於無法完成訂閱的情況,訂閱會引發錯誤,您可以在完成處理程序中捕獲該錯誤,如下所示:
迅速
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"); }];
此呼叫向 FCM 後端發出非同步請求,並向客戶端訂閱給定主題。在呼叫subscribeToTopic:topic
之前,請確保客戶端應用程式實例已透過回呼didReceiveRegistrationToken
收到註冊令牌。
每次應用程式啟動時,FCM 都會確保所有請求的主題均已訂閱。若要取消訂閱,請呼叫unsubscribeFromTopic:topic
,FCM 將在背景取消訂閱該主題。
管理伺服器上的主題訂閱
Firebase Admin SDK可讓您從伺服器端執行基本的主題管理任務。給定他們的註冊令牌,您可以使用伺服器邏輯批量訂閱和取消訂閱客戶端應用程式實例。
您可以將客戶端應用程式實例訂閱到任何現有主題,也可以建立新主題。當您使用 API 為用戶端套用訂閱新主題(您的 Firebase 專案尚不存在的主題)時,FCM 中會建立一個同名的新主題,任何客戶端都可以隨後訂閱該主題。
您可以將註冊令牌清單傳遞給 Firebase Admin SDK 訂閱方法,以將相應的裝置訂閱主題:
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);
});
爪哇
// 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')
去
// 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");
管理 FCM API 還允許您透過將註冊令牌傳遞給適當的方法來取消訂閱裝置的主題:
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);
});
爪哇
// 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')
去
// 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");
subscribeToTopic()
和unsubscribeFromTopic()
方法會產生一個包含來自 FCM 的回應的物件。無論請求中指定的註冊令牌數量如何,返回類型都具有相同的格式。
如果出現錯誤(身份驗證失敗、無效令牌或主題等),這些方法會導致錯誤。有關錯誤代碼的完整清單(包括說明和解決步驟),請參閱管理 FCM API 錯誤。
接收並處理主題訊息
FCM 傳遞主題訊息的方式與其他下游訊息相同。
實作application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
如下所示:
迅速
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); }
建構發送請求
建立主題後,您可以透過在客戶端訂閱該主題的客戶端應用程式實例或透過伺服器 API向該主題發送訊息。如果這是您第一次建置 FCM 的傳送請求,請參閱伺服器環境和 FCM指南以取得重要的背景和設定資訊。
在後端的發送邏輯中,指定所需的主題名稱,如下所示:
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);
});
爪哇
// 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)
去
// 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);
休息
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"
}
}
}
捲曲命令:
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
若要將訊息傳送到主題組合,請指定條件,它是指定目標主題的布林表達式。例如,以下條件將向訂閱TopicA
以及TopicB
或TopicC
的裝置發送訊息:
"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"
FCM 首先計算括號中的任何條件,然後從左到右計算表達式。在上面的表達式中,訂閱任何單一主題的用戶都不會收到訊息。同樣,未訂閱TopicA
用戶也不會收到該訊息。這些組合確實收到了它:
-
TopicA
和TopicB
-
TopicA
和TopicC
您最多可以在條件表達式中包含五個主題。
發送到條件:
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);
});
爪哇
// 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)
去
// 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);
休息
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",
}
}
}
捲曲命令:
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
下一步
- 了解有關發送到多個設備的其他方式的更多資訊 -設備群組訊息傳遞