如要將訊息傳送至多部裝置,請使用主題訊息。這項功能可讓您將訊息傳送至已選擇特定主題的多部裝置。
本教學課程著重於使用 Admin SDK 或 REST API,從應用程式伺服器傳送主題訊息至 FCM,並在 Android 應用程式中接收及處理這些訊息。我們會說明如何處理應用程式在背景和前景中收到的訊息。從設定到驗證,所有步驟都包含在內。
設定 SDK
如果您已設定 Android 用戶端應用程式以用於 FCM,或已完成傳送第一則訊息的步驟,本節可能包含您已完成的步驟。
事前準備
安裝或更新 Android Studio 至最新版本。
請確認專案符合下列規定 (請注意,部分產品可能會有更嚴格的規定):
- 以 API 級別 21 (Lollipop) 以上版本為目標版本
- 搭載 Android 5.0 以上版本
- 使用 Jetpack (AndroidX),包括符合下列版本需求:
com.android.tools.build:gradle
v7.3.0 以上版本compileSdkVersion
28 以上版本
設定實體裝置或使用模擬器執行應用程式。
請注意,依附於 Google Play 服務的 Firebase SDK 必須在已安裝 Google Play 服務的裝置或模擬器上執行。使用 Google 帳戶登入 Firebase。
如果您還沒有 Android 專案,只是想試用 Firebase 產品,可以下載我們的快速入門範例。
建立 Firebase 專案
將 Firebase 加入 Android 應用程式前,請先建立要連結至該 Android 應用程式的 Firebase 專案。如要進一步瞭解 Firebase 專案,請參閱「瞭解 Firebase 專案」。
建立 Firebase 專案
-
在 Firebase 控制台中,按一下「新增專案」。
-
如要將 Firebase 資源新增至現有 Google Cloud 專案,請輸入專案名稱或從下拉式選單中選取專案。
-
如要建立新專案,請輸入專案名稱。您也可以選擇編輯專案名稱下方顯示的專案 ID。
-
-
如果系統顯示提示,請詳閱並接受 Firebase 條款。
-
按一下「繼續」。
-
(選用) 為專案設定 Google Analytics,以便使用下列 Firebase 產品,獲得最佳體驗: Firebase A/B Testing、 Cloud Messaging、 Crashlytics、 In-App Messaging 和 Remote Config (包括個人化)。
選取現有Google Analytics帳戶或建立新帳戶。如果建立新帳戶,請選取Analytics報表位置,然後接受專案的資料共用設定和Google Analytics條款。
-
按一下「建立專案」 (或「新增 Firebase」,如果要在現有的 Google Cloud 專案中新增 Firebase)。
Firebase 會自動為您的 Firebase 專案佈建資源。完成程序後,系統會將您帶往 Firebase 控制台的 Firebase 專案總覽頁面。
向 Firebase 註冊應用程式
如要在 Android 應用程式中使用 Firebase,請向 Firebase 專案註冊應用程式。註冊應用程式通常稱為「將應用程式新增至專案」。
前往 Firebase 控制台。
在專案總覽頁面中間,按一下「Android」圖示 (
) 或「新增應用程式」,啟動設定工作流程。在「Android 套件名稱」欄位中,輸入應用程式的套件名稱。
什麼是套件名稱?如何找出套件名稱?
套件名稱可明確識別裝置和 Google Play 商店中的應用程式。
套件名稱通常稱為應用程式 ID。
在模組 (應用程式層級) Gradle 檔案中尋找應用程式的套件名稱,通常是
app/build.gradle
(套件名稱範例:com.yourcompany.yourproject
)。請注意,套件名稱值會區分大小寫,且在向 Firebase 專案註冊後,就無法變更這個 Firebase Android 應用程式的套件名稱。
(選用) 輸入其他應用程式資訊:「應用程式暱稱」和「偵錯簽署憑證 SHA-1」。
Firebase 如何使用「應用程式暱稱」和「偵錯簽署憑證 SHA-1」?
應用程式暱稱:內部使用的便利 ID,只會顯示在 Firebase 管理中心
偵錯簽署憑證 SHA-1:使用 Google 登入或電話號碼登入時,Firebase Authentication 和 Firebase Dynamic Links需要 SHA-1 雜湊。
按一下 [Register app] (註冊應用程式)。
新增 Firebase 設定檔
下載應用程式的 Firebase 設定檔 (
),然後新增至程式碼集:google-services.json 按一下「下載 google-services.json」,取得應用程式的 Firebase 設定檔。
將設定檔移到應用程式的模組 (應用程式層級) 根目錄中。
關於這個設定檔,您需要瞭解哪些事項?
Firebase 設定檔包含專案和應用程式的非祕密專屬 ID。如要進一步瞭解這個設定檔,請參閱「瞭解 Firebase 專案」。
您隨時可以再次下載 Firebase 設定檔。
請確定設定檔名稱未附加額外的字元,例如
(2)
。
如要讓 Firebase SDK 存取
設定檔中的值,您需要Google 服務 Gradle 外掛程式 (google-services.json google-services
)。在根層級 (專案層級) 的 Gradle 檔案 (
<project>/build.gradle.kts
或<project>/build.gradle
) 中,將 Google 服務外掛程式新增為依附元件: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.3" apply false }
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.3' apply false }
在模組 (應用程式層級) Gradle 檔案 (通常為
<project>/<app-module>/build.gradle.kts
或<project>/<app-module>/build.gradle
) 中,加入 Google 服務外掛程式:plugins { id("com.android.application") // Add the Google services Gradle plugin id("com.google.gms.google-services") // ... }
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.16.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 程式庫版本。
想尋找 Kotlin 專用的程式庫模組嗎?自 2023 年 10 月 (Firebase BoM 32.5.0) 起,Kotlin 和 Java 開發人員都可以依附於主要程式庫模組 (詳情請參閱這項計畫的常見問題)。(替代做法) 不使用 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.2") implementation("com.google.firebase:firebase-analytics:22.5.0") }
將 Android 專案與 Gradle 檔案同步。
您是否收到有關叫用自訂支援和啟用去糖化的建構失敗訊息?修正方式如下:
使用 Android Gradle 外掛程式 (AGP) 4.2 以下版本的 Gradle 建構作業,需要啟用 Java 8 支援。否則,這些 Android 專案在新增 Firebase SDK 時會發生建構失敗。
如要修正這個建構失敗問題,可以採取下列任一做法:
- 將錯誤訊息中列出的
compileOptions
新增至應用程式層級的build.gradle.kts
或build.gradle
檔案。 - 將 Android 專案的
minSdk
提高至 26 以上。
如要進一步瞭解建構失敗,請參閱這篇常見問題。
- 將錯誤訊息中列出的
讓用戶端應用程式訂閱主題
用戶端應用程式可以訂閱任何現有主題,也可以建立新主題。當用戶端應用程式訂閱新的主題名稱 (Firebase 專案中尚未存在的主題),系統會在 FCM 中建立該名稱的新主題,之後任何用戶端都能訂閱。
如要訂閱主題,用戶端應用程式會使用 FCM 主題名稱呼叫 Firebase Cloud Messaging
subscribeToTopic()
。這個方法會傳回 Task
,完成事件監聽器可用於判斷訂閱是否成功:
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() }
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
回呼。
視呼叫 onMessageReceived
前發生的延遲情形 (包括 OS 延遲、應用程式啟動時間、主執行緒遭其他作業阻斷,或先前呼叫 onMessageReceived
花費過長時間),處理訊息的時間範圍可能短於 20 秒。之後,Android 的程序終止或 Android O 的
背景執行限制等各種 OS 行為,可能會干擾您完成工作。
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 物件執行動作,並取得訊息資料:
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. }
@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,FCM 就不會撥打電話給 onDeletedMessages()
。
處理背景應用程式中的通知訊息
當應用程式在背景執行時,Android 會將通知訊息導向系統匣。使用者輕觸通知後,系統預設會開啟應用程式啟動器。
包括同時含有通知和資料酬載的訊息 (以及從「通知」控制台傳送的所有訊息)。在這些情況下,通知會傳送到裝置的系統匣,資料酬載則會傳送到啟動器 Activity 的 Intent 額外內容中。
如要深入瞭解訊息傳送至應用程式的情況,請參閱 FCM報表資訊主頁,其中會記錄在 Apple 和 Android 裝置上傳送及開啟的訊息數量,以及 Android 應用程式的「曝光次數」(使用者看到的通知) 資料。
建立傳送要求
建立主題後 (方法是在用戶端訂閱主題,或透過伺服器 API),即可將訊息傳送至該主題。如果您是第一次為 FCM 建立傳送要求,請參閱伺服器環境和 FCM 的指南,瞭解重要的背景和設定資訊。
在後端的傳送邏輯中,指定所需的主題名稱,如下所示:
// 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);
# 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)
// 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 指令:
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
條件式運算式最多可包含五個主題。
如要傳送至條件:
// 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);
# 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)
// 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 指令:
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
後續步驟
- 您可以使用伺服器訂閱主題的用戶端應用程式執行個體,以及執行其他管理工作。請參閱「在伺服器上管理主題訂閱項目」。