Catch up on everthing we announced at this year's Firebase Summit. Learn more

向多個設備發送消息

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

  • 主題訊息,它允許你將消息發送到具有特定主題選擇多個設備。
  • 設備組的消息,它允許你將消息發送到屬於您定義的多台設備。

本教程主要介紹如何使用您的應用服務器發送主題的消息聯繫SDKREST API的FCM,以及接收和Android應用中處理它們。我們將介紹後台和前台應用程序的消息處理。涵蓋了實現這一目標的所有步驟,從設置到驗證。

設置 SDK

這部分可能包括如果您有已經完成的步驟設置Android客戶端應用程序的FCM或一步步地努力要發送的第一條消息

在你開始之前

如果你不已經有一個Android項目,只是想嘗試一個火力地堡的產品,你可以下載我們的一個入門示例

創建 Firebase 項目

在將 Firebase 添加到您的 Android 應用之前,您需要創建一個 Firebase 項目以連接到您的 Android 應用。參觀了解火力地堡項目,詳細了解項目的火力地堡。

向 Firebase 註冊您的應用

要在您的 Android 應用中使用 Firebase,您需要向您的 Firebase 項目註冊您的應用。註冊您的應用程序通常稱為將您的應用程序“添加”到您的項目中。

  1. 轉至火力地堡控制台

  2. 在項目概述頁面的中心,點擊圖標的Android( )或添加應用程序來啟動安裝工作流程。

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

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

  5. 點擊註冊應用

添加 Firebase 配置文件

  1. 將 Firebase Android 配置文件添加到您的應用:

    1. 點擊下載谷歌,services.json獲得你的火力地堡的Android配置文件( google-services.json )。

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

  2. 為使火力地堡的產品在你的應用程序,添加了谷歌的服務插件到您的搖籃文件。

    1. 在你的根級別(項目級別)搖籃文件( build.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.10'  // Google Services plugin
        }
      }
      
      allprojects {
        // ...
      
        repositories {
          // Check that you have the following line (if not, add it):
          google()  // Google's Maven repository
          // ...
        }
      }
      
    2. 在你的模塊(應用程序級)搖籃文件(通常是app/build.gradle ),應用谷歌服務搖籃插件:

      apply plugin: 'com.android.application'
      // Add the following line:
      apply plugin: 'com.google.gms.google-services'  // Google Services plugin
      
      android {
        // ...
      }
      

將 Firebase SDK 添加到您的應用

  1. 使用火力地堡Android的物料清單,聲明你的模塊(應用程序級)搖籃文件(通常為火力地堡雲端通訊的Android庫的依賴app/build.gradle )。

    對於火力地堡雲端通訊獲得最佳體驗,我們建議讓谷歌Analytics(分析)在您的火力地堡項目,並添加火力地堡SDK為谷歌Analytics(分析)應用程式。

    爪哇

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

    通過使用火力地堡Android的物料清單,您的應用程序將始終使用火力地堡的Android庫的兼容版本。

    (替代)聲明火力地堡庫依賴使用物料清單

    如果您選擇不使用 Firebase BoM,則必須在其依賴項行中指定每個 Firebase 庫版本。

    需要注意的是,如果你在你的應用程序使用多個火力地堡庫,我們強烈建議您使用的物料清單管理庫版本,以保證所有版本相互兼容。

    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.0'
        implementation 'com.google.firebase:firebase-analytics:20.0.0'
    }
    

    科特林+KTX

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

    通過使用火力地堡Android的物料清單,您的應用程序將始終使用火力地堡的Android庫的兼容版本。

    (替代)聲明火力地堡庫依賴使用物料清單

    如果您選擇不使用 Firebase BoM,則必須在其依賴項行中指定每個 Firebase 庫版本。

    需要注意的是,如果你在你的應用程序使用多個火力地堡庫,我們強烈建議您使用的物料清單管理庫版本,以保證所有版本相互兼容。

    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.0'
        implementation 'com.google.firebase:firebase-analytics-ktx:20.0.0'
    }
    

  2. 同步您的應用程序以確保所有依賴項都具有必要的版本。

將客戶端應用程序訂閱到主題

客戶端應用程序可以訂閱任何現有主題,也可以創建新主題。當客戶端應用訂閱新主題名稱(您的 Firebase 項目尚不存在的主題名稱)時,會在 FCM 中創建該名稱的新主題,隨後任何客戶端都可以訂閱它。

要訂閱一個話題,客戶端應用程序調用火力地堡雲端通訊subscribeToTopic()與FCM主題名稱。該方法返回一個Task ,這可以通過完成監聽器被用來確定認購是否成功:

爪哇

FirebaseMessaging.getInstance().subscribeToTopic("weather")
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                String msg = getString(R.string.msg_subscribed);
                if (!task.isSuccessful()) {
                    msg = getString(R.string.msg_subscribe_failed);
                }
                Log.d(TAG, msg);
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });

科特林+KTX

Firebase.messaging.subscribeToTopic("weather")
        .addOnCompleteListener { task ->
            var msg = getString(R.string.msg_subscribed)
            if (!task.isSuccessful) {
                msg = getString(R.string.msg_subscribe_failed)
            }
            Log.d(TAG, msg)
            Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
        }

要取消訂閱,客戶端應用程序調用火力地堡雲端通訊unsubscribeFromTopic()以主題名稱。

接收和處理主題消息

FCM 以與其他下游消息相同的方式傳遞主題消息。

要接收郵件,使用擴展服務FirebaseMessagingService 。您的服務應覆蓋onMessageReceivedonDeletedMessages回調。它應該在收到後 20 秒內處理任何消息(Android Marshmallow 為 10 秒)。根據發生的提前調用OS延遲的時間窗口可能會短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 顯示自定義默認圖標

  • 從發送的所有通知消息通知作曲家
  • 未在通知負載中明確設置圖標的任何通知消息。

Android 使用自定義默認顏色

  • 從發送的所有通知消息通知作曲家
  • 未在通知負載中明確設置顏色的任何通知消息。

如果未設置自定義默認圖標且未在通知負載中設置圖標,Android 將顯示呈現為白色的應用程序圖標。

覆蓋onMessageReceived

通過重載方法FirebaseMessagingService.onMessageReceived ,您可以執行基於接收到的上行動RemoteMessage對象並獲取消息數據:

爪哇

@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.
}

科特林+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報告信息中心,它記錄發送和“印象”(用戶看到通知)for Android應用程序打開蘋果和Android設備,伴隨數據的消息的數量。

後台受限應用(Android P 或更新版本)

FCM可能無法提供消息,向其中放入應用程序的背景限制由用戶(例如通過:設置- >應用程序和通知- > [APPNAME] - >電池)。一旦您的應用程序從後台限制中刪除,新消息將像以前一樣發送到應用程序。為了防止丟失的郵件和其他背景限制的影響,一定要避免由上市不良行為的Android命脈的努力。這些行為可能會導致 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並且或者TopicBTopicC

"'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

下一步