Firebase 雲消息傳遞提供了兩種將消息定位到多個設備的方法:
本教程重點介紹使用Admin SDK或REST API for FCM 從您的應用服務器發送主題消息,以及在 android 應用中接收和處理它們。我們將介紹後台和前台應用程序的消息處理。涵蓋了從設置到驗證的所有步驟。
設置 SDK
如果您為 FCM設置了 Android 客戶端應用程序或完成了發送您的第一條消息的步驟,則本部分可能涵蓋您已經完成的步驟。
在你開始之前
安裝或更新Android Studio到最新版本。
確保您的項目滿足以下要求:
- 針對 API 級別 19 (KitKat) 或更高級別
- 使用 Android 4.4 或更高版本
- 使用Jetpack (AndroidX) ,其中包括滿足以下版本要求:
-
com.android.tools.build:gradle
v3.2.1 或更高版本 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 包名稱字段中輸入您應用的包名稱。
包名稱在設備上和 Google Play 商店中唯一標識您的應用程序。
包名稱通常稱為應用程序 ID 。
在模塊(應用級)Gradle 文件中找到應用的包名稱,通常是
app/build.gradle
(示例包名稱:com.yourcompany.yourproject
)。請注意,包名稱值區分大小寫,並且在此 Firebase Android 應用註冊到您的 Firebase 項目後無法更改它。
(可選)輸入其他應用信息:應用暱稱和調試簽名證書 SHA-1 。
應用暱稱:僅在 Firebase 控制台中對您可見的內部便利標識符
調試簽名證書 SHA-1 :Firebase 身份驗證(使用Google 登錄或電話號碼登錄時)和Firebase 動態鏈接需要SHA-1 哈希。
點擊註冊應用。
添加 Firebase 配置文件
將 Firebase Android 配置文件添加到您的應用中:
單擊下載 google-services.json以獲取您的 Firebase Android 配置文件 (
)。google-services.json 將您的配置文件移動到應用程序的模塊(應用程序級)目錄中。
Firebase 配置文件包含項目的唯一但非秘密標識符。要了解有關此配置文件的更多信息,請訪問了解 Firebase 項目。
您可以隨時再次下載Firebase 配置文件。
確保配置文件名沒有附加其他字符,例如
(2)
。
要在您的應用中啟用 Firebase 產品,請將google-services 插件添加到您的 Gradle 文件中。
在您的根級(項目級)Gradle 文件 (
build.gradle
) 中,添加規則以包含 Google Services Gradle 插件。檢查您是否也有 Google 的 Maven 存儲庫。buildscript { repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository } dependencies { // ... // Add the following line: classpath 'com.google.gms:google-services:4.3.13' // Google Services plugin } } allprojects { // ... repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository // ... } }
在您的模塊(應用級)Gradle 文件(通常是
app/build.gradle
)中,應用 Google Services Gradle 插件:apply plugin: 'com.android.application' // Add the following line: apply plugin: 'com.google.gms.google-services' // Google Services plugin android { // ... }
將 Firebase SDK 添加到您的應用
使用Firebase Android BoM ,在您的模塊(應用級)Gradle 文件(通常是
app/build.gradle
)中聲明 Firebase Cloud Messaging Android 庫的依賴項。為了獲得 Firebase 雲消息傳遞的最佳體驗,我們建議在您的 Firebase 項目中啟用 Google Analytics ,並將 Firebase SDK for Google Analytics 添加到您的應用中。
Java
dependencies { // Import the BoM for the Firebase platform implementation platform('com.google.firebase:firebase-bom:30.1.0') // Declare 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 { // Declare 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:23.0.6' implementation 'com.google.firebase:firebase-analytics:21.0.0' }
Kotlin+KTX
dependencies { // Import the BoM for the Firebase platform implementation platform('com.google.firebase:firebase-bom:30.1.0') // Declare 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-ktx' implementation 'com.google.firebase:firebase-analytics-ktx' }
通過使用Firebase Android BoM ,您的應用將始終使用兼容版本的 Firebase Android 庫。
(替代)在不使用 BoM 的情況下聲明 Firebase 庫依賴項
如果您選擇不使用 Firebase BoM,則必須在其依賴行中指定每個 Firebase 庫版本。
請注意,如果您在應用中使用多個Firebase 庫,我們強烈建議您使用 BoM 來管理庫版本,以確保所有版本都兼容。
dependencies { // Declare 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-ktx:23.0.6' implementation 'com.google.firebase:firebase-analytics-ktx:21.0.0' }
同步您的應用程序以確保所有依賴項都具有必要的版本。
使用 Android Gradle 插件 (AGP) v4.2 或更早版本的 Gradle 構建需要啟用 Java 8 支持。否則,這些 Android 項目在添加 Firebase SDK 時會出現構建失敗。
要修復此構建失敗,您可以遵循以下兩個選項之一:
- 將錯誤消息中列出的
compileOptions
添加到您的應用程序級build.gradle
文件中。 - 將您的 Android 項目的
minSdkVersion
增加到 26 或更高。
在此常見問題解答中了解有關此構建失敗的更多信息。
- 將錯誤消息中列出的
為客戶端應用訂閱主題
客戶端應用程序可以訂閱任何現有主題,也可以創建新主題。當客戶端應用訂閱新主題名稱(您的 Firebase 項目尚不存在的主題名稱)時,會在 FCM 中創建該名稱的新主題,隨後任何客戶端都可以訂閱它。
要訂閱主題,客戶端應用程序使用 FCM 主題名稱調用 Firebase 雲消息傳遞subscribeToTopic()
。此方法返回一個Task
,完成偵聽器可以使用它來確定訂閱是否成功:
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(); } });
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() }
要取消訂閱,客戶端應用程序使用主題名稱調用 Firebase Cloud Messaging unsubscribeFromTopic()
。
接收和處理主題消息
FCM 以與其他下游消息相同的方式傳遞主題消息。
要接收消息,請使用擴展FirebaseMessagingService的服務。您的服務應覆蓋onMessageReceived
和onDeletedMessages
回調。它應該在收到後 20 秒內處理任何消息(Android Marshmallow 為 10 秒)。時間窗口可能會更短,具體取決於調用onMessageReceived
之前發生的操作系統延遲。在那之後,各種操作系統行為(例如 Android O 的後台執行限制)可能會干擾您完成工作的能力。有關更多信息,請參閱我們的消息優先級概述。
大多數消息類型都提供了onMessageReceived
,但以下情況除外:
當您的應用程序在後台時傳遞的通知消息。在這種情況下,通知將傳送到設備的系統托盤。默認情況下,用戶點擊通知會打開應用啟動器。
在後台接收時帶有通知和數據負載的消息。在這種情況下,通知被傳遞到設備的系統托盤,數據負載被傳遞到啟動器 Activity 的附加內容中。
總之:
應用狀態 | 通知 | 數據 | 兩個都 |
---|---|---|---|
前景 | 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 顯示自定義默認圖標
- 從Notifications composer發送的所有通知消息。
- 任何未在通知有效負載中明確設置圖標的通知消息。
Android 使用自定義默認顏色
- 從Notifications composer發送的所有通知消息。
- 任何未在通知負載中明確設置顏色的通知消息。
如果未設置自定義默認圖標且未在通知負載中設置圖標,則 Android 會顯示呈現為白色的應用程序圖標。
覆蓋onMessageReceived
通過覆蓋FirebaseMessagingService.onMessageReceived
方法,您可以根據接收到的RemoteMessage對象執行操作並獲取消息數據:
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. }
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}") 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. 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. }
覆蓋onDeletedMessages
在某些情況下,FCM 可能不會傳遞消息。如果您的應用在連接時在特定設備上等待處理的消息過多 (>100),或者如果該設備超過一個月未連接到 FCM,則會發生這種情況。在這些情況下,您可能會收到對FirebaseMessagingService.onDeletedMessages()
的回調。當應用實例收到此回調時,它應該與您的應用服務器執行完全同步。如果您在過去 4 週內未向該設備上的應用發送消息,FCM 將不會調用onDeletedMessages()
。在後台應用程序中處理通知消息
當您的應用程序處於後台時,Android 會將通知消息定向到系統托盤。默認情況下,用戶點擊通知會打開應用啟動器。
這包括同時包含通知和數據負載的消息(以及從通知控制台發送的所有消息)。在這些情況下,通知會被傳遞到設備的系統托盤,而數據負載會在啟動器 Activity 的意圖的附加部分中傳遞。
要深入了解向您的應用程序傳遞的消息,請參閱FCM 報告儀表板,它記錄了在 Apple 和 Android 設備上發送和打開的消息數量,以及 Android 應用程序的“展示次數”(用戶看到的通知)的數據。
後台受限應用(Android P 或更新版本)
FCM 可能不會向用戶設置後台限制的應用程序發送消息(例如通過:設置 -> 應用程序和通知 -> [應用程序名稱] -> 電池)。一旦您的應用程序從後台限制中刪除,新消息將像以前一樣傳遞到該應用程序。為了防止丟失消息和其他後台限制影響,請確保避免Android Vitals努力列出的不良行為。這些行為可能會導致 Android 設備向用戶建議您的應用受後台限制。你的應用可以使用isBackgroundRestricted()檢查它是否被後台限制。構建發送請求
創建主題後,通過在客戶端訂閱客戶端應用程序實例或通過服務器 API訂閱主題,您可以向該主題發送消息。如果這是您第一次為 FCM 構建發送請求,請參閱服務器環境和 FCM指南以獲取重要的背景和設置信息。
在後端的發送邏輯中,指定所需的主題名稱,如下所示:
節點.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
您最多可以在條件表達式中包含五個主題。
發送到條件:
節點.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
下一步
- 您可以使用服務器為客戶端應用程序實例訂閱主題並執行其他管理任務。請參閱管理服務器上的主題訂閱。
- 詳細了解發送到多個設備的另一種方式 —設備群組消息