Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

向多個設備發送消息

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

Firebase 雲消息傳遞提供了兩種將消息定位到多個設備的方法:

  • 主題消息傳遞,它允許您向已選擇加入特定主題的多個設備發送消息。
  • 設備組消息,允許您向屬於您定義的組的多個設備發送消息。

本教程重點介紹使用Admin SDKREST 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 項目中註冊您的應用。註冊您的應用程序通常稱為將您的應用程序“添加”到您的項目中。

  1. 轉到Firebase 控制台

  2. 在項目概覽頁面的中心,單擊Android圖標 ( ) 或添加應用程序以啟動設置工作流程。

  3. Android 包名稱字段中輸入您應用的包名稱。

  4. (可選)輸入其他應用信息:應用暱稱調試簽名證書 SHA-1

  5. 點擊註冊應用

添加 Firebase 配置文件

  1. 下載 Firebase Android 配置文件 ( google-services.json ) 並將其添加到您的應用:

    1. 單擊下載 google-services.json以獲取您的 Firebase Android 配置文件。

    2. 將您的配置文件移動到應用程序的模塊(應用程序級)根目錄中。

  2. 要使 Firebase SDK 可以訪問google-services.json配置文件中的值,您需要Google 服務 Gradle 插件( google-services )。

    1. 在您的根級(項目級) Gradle 文件 ( <project>/build.gradle ) 中,將 Google 服務插件添加為 buildscript 依賴項:

      buildscript {
      
          repositories {
            // Make sure that you have the following two repositories
            google()  // Google's Maven repository
            mavenCentral()  // Maven Central repository
          }
      
          dependencies {
            ...
      
            // Add the dependency for the Google services Gradle plugin
            classpath 'com.google.gms:google-services:4.3.14'
          }
      }
      
      allprojects {
        ...
      
        repositories {
          // Make sure that you have the following two repositories
          google()  // Google's Maven repository
          mavenCentral()  // Maven Central repository
        }
      }
      
    2. 在您的模塊(應用級) Gradle 文件(通常是<project>/<app-module>/build.gradle )中,添加 Google 服務插件:

      plugins {
          id 'com.android.application'
      
          // Add the Google services Gradle plugin
          id 'com.google.gms.google-services'
          ...
      }
      

將 Firebase SDK 添加到您的應用

  1. 在您的模塊(應用級)Gradle 文件(通常是<project>/<app-module>/build.gradle )中,添加 Firebase Cloud Messaging Android 庫的依賴項。我們建議使用Firebase Android BoM來控制庫版本控制。

    為了獲得 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:31.1.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:23.1.0'
        implementation 'com.google.firebase:firebase-analytics:21.2.0'
    }
    

    Kotlin+KTX

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:31.1.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-ktx'
        implementation 'com.google.firebase:firebase-analytics-ktx'
    }
    

    通過使用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-ktx:23.1.0'
        implementation 'com.google.firebase:firebase-analytics-ktx:21.2.0'
    }
    

  2. 將您的 Android 項目與 Gradle 文件同步。

為客戶端應用訂閱主題

客戶端應用程序可以訂閱任何現有主題,也可以創建新主題。當客戶端應用訂閱新主題名稱(您的 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的服務。您的服務應覆蓋onMessageReceivedonDeletedMessages回調。它應該在收到後 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

要將消息發送到主題組合,請指定條件,它是指定目標主題的布爾表達式。例如,以下條件將向訂閱了TopicATopicBTopicC的設備發送消息:

"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"

FCM 首先計算括號中的任何條件,然後從左到右計算表達式。在上面的表達式中,訂閱任何單個主題的用戶都不會收到消息。同樣,未訂閱TopicA的用戶也不會收到該消息。這些組合確實收到了它:

  • TopicATopicB
  • TopicATopicC

您最多可以在條件表達式中包含五個主題。

發送到條件:

節點.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

下一步