如要將訊息指定給多部裝置,請使用主題訊息傳送功能。這項功能可讓你向已選擇加入特定主題的多部裝置傳送訊息。
本教學課程著重於使用 Admin SDK 或 FCM 的 REST API,從應用程式伺服器傳送主題訊息,並在 Android 應用程式中接收及處理這些訊息。我們將說明背景和前景應用程式的訊息處理方式。從設定到驗證,我們會說明達成此目標的所有步驟。
設定 SDK
如果您已為 FCM 設定 Android 用戶端應用程式,或已完成傳送第一則訊息的步驟,本節可能會涵蓋您已完成的步驟。
事前準備
安裝或更新至最新版的 Android Studio。
請確認專案符合下列規定 (請注意,部分產品可能有更嚴格的規定):
- 指定 API 級別 21 (Lollipop) 以上版本
- 使用 Android 5.0 以上版本
- 使用 Jetpack (AndroidX),包括符合下列版本需求:
com.android.tools.build:gradle
7.3.0 以上版本compileSdkVersion
28 以上版本
設定實體裝置或使用模擬器執行應用程式。
請注意,依附於 Google Play 服務的 Firebase SDK 需要在裝置或模擬器上安裝 Google Play 服務。使用 Google 帳戶登入 Firebase。
如果您還沒有 Android 專案,但想試用 Firebase 產品,可以下載我們的快速入門範例。
建立 Firebase 專案
如要將 Firebase 新增至 Android 應用程式,您必須先建立 Firebase 專案,才能連結至 Android 應用程式。請參閱「瞭解 Firebase 專案」,進一步瞭解 Firebase 專案。
透過 Firebase 註冊應用程式
如要在 Android 應用程式中使用 Firebase,您必須將應用程式註冊至 Firebase 專案。註冊應用程式通常稱為將應用程式「新增」至專案。
前往 Firebase 控制台。
在專案總覽頁面的中間,按一下「Android」圖示 (
) 或「新增應用程式」,啟動設定工作流程。在「Android 套件名稱」欄位中輸入應用程式的套件名稱。
(選用) 輸入其他應用程式資訊:應用程式暱稱和偵錯簽署憑證 SHA-1。
按一下 [Register app] (註冊應用程式)。
新增 Firebase 設定檔
請下載 Firebase Android 設定檔 (
),然後將其新增至應用程式:google-services.json 按一下「Download google-services.json」,取得 Firebase Android 設定檔。
將設定檔移至應用程式的模組 (應用程式層級) 根目錄。
須有 Google 服務 Gradle 外掛程式 (
google-services
),Firebase SDK 才能存取
設定檔中的值。google-services.json 在根層級 (專案層級) Gradle 檔案 (
<project>/build.gradle.kts
或<project>/build.gradle
) 中,將 Google 服務外掛程式新增為依附元件:Kotlin
plugins { id("com.android.application") version "7.3.0" apply false // ... // Add the dependency for the Google services Gradle plugin id("com.google.gms.google-services") version "4.4.2" apply false }
Groovy
plugins { id 'com.android.application' version '7.3.0' apply false // ... // Add the dependency for the Google services Gradle plugin id 'com.google.gms.google-services' version '4.4.2' apply false }
在模組 (應用程式層級) Gradle 檔案 (通常為
<project>/<app-module>/build.gradle.kts
或<project>/<app-module>/build.gradle
) 中,新增 Google 服務外掛程式:Kotlin
plugins { id("com.android.application") // Add the Google services Gradle plugin id("com.google.gms.google-services") // ... }
Groovy
plugins { id 'com.android.application' // Add the Google services Gradle plugin id 'com.google.gms.google-services' // ... }
在應用程式中新增 Firebase SDK
在模組 (應用程式層級) Gradle 檔案 (通常為
<project>/<app-module>/build.gradle.kts
或<project>/<app-module>/build.gradle
) 中,加入 Android 的 Firebase Cloud Messaging 程式庫依附元件。建議您使用 Firebase Android BoM 來控制程式庫版本。為提供最佳的 Firebase Cloud Messaging 體驗,建議您在 Firebase 專案中啟用 Google Analytics,並將 Google Analytics 專用 Firebase SDK 新增至應用程式。
dependencies { // Import the BoM for the Firebase platform implementation(platform("com.google.firebase:firebase-bom:33.6.0")) // Add the dependencies for the Firebase Cloud Messaging and Analytics libraries // When using the BoM, you don't specify versions in Firebase library dependencies implementation("com.google.firebase:firebase-messaging") implementation("com.google.firebase:firebase-analytics") }
只要使用 Firebase Android BoM,應用程式就會一律使用相容的 Firebase Android 程式庫版本。
(替代做法) 不使用 BoM 新增 Firebase 程式庫依附元件
如果您選擇不使用 Firebase BoM,則必須在依附元件行中指定每個 Firebase 程式庫版本。
請注意,如果您在應用程式中使用多個 Firebase 程式庫,強烈建議您使用 BoM 來管理程式庫版本,確保所有版本皆相容。
dependencies { // Add the dependencies for the Firebase Cloud Messaging and Analytics libraries // When NOT using the BoM, you must specify versions in Firebase library dependencies implementation("com.google.firebase:firebase-messaging:24.1.0") implementation("com.google.firebase:firebase-analytics:22.1.2") }
將 Android 專案與 Gradle 檔案同步處理。
讓用戶端應用程式訂閱主題
用戶端應用程式可以訂閱任何現有主題,也可以建立新主題。當用戶端應用程式訂閱新主題名稱 (在您的 Firebase 專案中不存在) 時,系統會在 FCM 中建立該名稱的新主題,之後任何用戶端都可以訂閱該主題。
如要訂閱主題,用戶端應用程式會使用 FCM 主題名稱呼叫 Firebase Cloud Messaging
subscribeToTopic()
。這個方法會傳回 Task
,可供完成事件監聽器用來判斷訂閱是否成功:
Kotlin+KTX
Firebase.messaging.subscribeToTopic("weather") .addOnCompleteListener { task -> var msg = "Subscribed" if (!task.isSuccessful) { msg = "Subscribe failed" } Log.d(TAG, msg) Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() }
Java
FirebaseMessaging.getInstance().subscribeToTopic("weather") .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { String msg = "Subscribed"; if (!task.isSuccessful()) { msg = "Subscribe failed"; } Log.d(TAG, msg); Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); } });
如要取消訂閱,用戶端應用程式會呼叫 Firebase Cloud Messaging unsubscribeFromTopic()
,並傳入主題名稱。
接收及處理主題訊息
FCM 會以與其他下游訊息相同的方式傳送主題訊息。
如要接收訊息,請使用擴充
FirebaseMessagingService
的服務。您的服務應覆寫 onMessageReceived
和 onDeletedMessages
回呼。
處理訊息的時間視窗可能會短於 20 秒,這取決於呼叫 onMessageReceived
前發生的延遲時間,包括作業系統延遲、應用程式啟動時間、主執行緒遭其他作業阻斷,或先前 onMessageReceived
呼叫耗費過多時間。超過這段時間後,各種 OS 行為 (例如 Android 的程序終止或 Android O 的
背景執行限制) 可能會影響您完成工作的可能性。
onMessageReceived
適用於大多數郵件類型,但以下類型除外:
-
在應用程式於背景運作期間傳送通知訊息。在這種情況下,系統會將通知傳送至裝置的系統匣。使用者輕觸通知後,系統預設會開啟應用程式啟動器。
-
在背景接收的訊息,其中包含通知和資料酬載。在這種情況下,系統會將通知傳送至裝置的系統通知格,並在啟動器活動的意圖額外項目中傳送資料酬載。
簡單來說:
應用程式狀態 | 通知 | 資料 | 兩者並用 |
---|---|---|---|
前景 | onMessageReceived |
onMessageReceived |
onMessageReceived |
背景 | 系統匣 | onMessageReceived |
通知:系統通知區 資料:意圖的額外資料。 |
編輯應用程式資訊清單
如要使用 FirebaseMessagingService
,您必須在應用程式資訊清單中新增下列內容:
<service android:name=".java.MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
此外,建議您設定預設值,自訂通知的外觀。您可以指定自訂預設圖示和自訂預設顏色,在通知酬載中未設定等值時套用。
在 application
標記中新增以下行,設定自訂預設圖示和自訂顏色:
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages. See README(https://goo.gl/l4GJaQ) for more. --> <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/ic_stat_ic_notification" /> <!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message. See README(https://goo.gl/6BKBk7) for more. --> <meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" />
Android 會顯示自訂預設圖示
- 所有透過 通知編輯器傳送的通知訊息。
- 任何未在通知酬載中明確設定圖示的通知訊息。
Android 會使用自訂預設顏色
- 所有透過 通知編輯器傳送的通知訊息。
- 任何未在通知酬載中明確設定顏色的通知訊息。
如果未設定自訂的預設圖示,且通知酬載中也未設定圖示,Android 會顯示以白色顯示的應用程式圖示。
覆寫 onMessageReceived
您可以覆寫 FirebaseMessagingService.onMessageReceived
方法,根據收到的 RemoteMessage 物件執行動作,並取得訊息資料:
Kotlin+KTX
override fun onMessageReceived(remoteMessage: RemoteMessage) { // TODO(developer): Handle FCM messages here. // Not getting messages here? See why this may be: https://goo.gl/39bRNJ Log.d(TAG, "From: ${remoteMessage.from}") // Check if message contains a data payload. if (remoteMessage.data.isNotEmpty()) { Log.d(TAG, "Message data payload: ${remoteMessage.data}") // Check if data needs to be processed by long running job if (needsToBeScheduled()) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleJob() } else { // Handle message within 10 seconds handleNow() } } // Check if message contains a notification payload. remoteMessage.notification?.let { Log.d(TAG, "Message Notification Body: ${it.body}") } // Also if you intend on generating your own notifications as a result of a received FCM // message, here is where that should be initiated. See sendNotification method below. }
Java
@Override public void onMessageReceived(RemoteMessage remoteMessage) { // TODO(developer): Handle FCM messages here. // Not getting messages here? See why this may be: https://goo.gl/39bRNJ Log.d(TAG, "From: " + remoteMessage.getFrom()); // Check if message contains a data payload. if (remoteMessage.getData().size() > 0) { Log.d(TAG, "Message data payload: " + remoteMessage.getData()); if (/* Check if data needs to be processed by long running job */ true) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleJob(); } else { // Handle message within 10 seconds handleNow(); } } // Check if message contains a notification payload. if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); } // Also if you intend on generating your own notifications as a result of a received FCM // message, here is where that should be initiated. See sendNotification method below. }
覆寫 onDeletedMessages
在某些情況下,FCM 可能無法傳送訊息。發生這種情況的原因是,在特定裝置連線時,應用程式有太多 (超過 100 則) 待處理訊息,或是裝置在一個多月內未連線至 FCM。在這些情況下,您可能會收到 FirebaseMessagingService.onDeletedMessages()
的回呼。當應用程式執行個體收到這項回呼時,應會與應用程式伺服器執行完整同步處理。如果您在過去 4 週內未向該裝置上的應用程式傳送訊息,FCM 就不會呼叫 onDeletedMessages()
。在背景應用程式中處理通知訊息
當應用程式處於背景執行時,Android 會將通知訊息導向系統匣。使用者輕觸通知後,系統會根據預設開啟應用程式啟動器。
這包括同時包含通知和資料酬載的訊息 (以及透過「通知」資訊主頁傳送的所有訊息)。在這些情況下,系統會將通知傳送至裝置的系統通知區,並在啟動器活動的意圖額外項目中傳送資料酬載。
如要深入瞭解應用程式訊息的傳送情形,請參閱 FCM 報表資訊主頁,這可記錄在 Apple 和 Android 裝置上傳送及開啟的訊息數量,以及 Android 應用程式「曝光次數」(使用者看到的通知) 資料。
建立傳送要求
建立主題後,您可以透過訂閱用戶端應用程式執行個體,或透過伺服器 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);
});
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);
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"
}
}
}
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
如要將訊息傳送至主題組合,請指定條件,這是指定目標主題的布林運算式。舉例來說,下列條件會將訊息傳送至已訂閱 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);
});
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);
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",
}
}
}
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
後續步驟
- 您可以使用伺服器讓用戶端應用程式執行緒訂閱主題,並執行其他管理工作。請參閱「管理伺服器上的主題訂閱項目」。