Отправка сообщений на несколько устройств

Чтобы отправить сообщение на несколько устройств, используйте функцию «Тематическая рассылка» . Эта функция позволяет отправлять сообщения на несколько устройств, на которых выбрана определённая тема.

В этом руководстве мы рассмотрим отправку сообщений с сервера приложений с помощью 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 или более поздняя версия
  • Настройте физическое устройство или используйте эмулятор для запуска вашего приложения.
    Обратите внимание, что для Firebase SDK, зависящих от сервисов Google Play, на устройстве или эмуляторе должны быть установлены сервисы Google Play.

  • Войдите в Firebase, используя свою учетную запись Google.

Если у вас еще нет проекта Android и вы просто хотите опробовать продукт Firebase, вы можете загрузить один из наших примеров быстрого старта .

Создать проект Firebase

Прежде чем добавить Firebase в приложение для Android, необходимо создать проект Firebase для подключения к нему. Подробнее о проектах Firebase можно узнать в разделе «Понимание проектов Firebase».

Зарегистрируйте свое приложение в Firebase

Чтобы использовать Firebase в вашем приложении для Android, вам необходимо зарегистрировать его в проекте Firebase. Регистрация приложения часто называется «добавлением» приложения в проект.

  1. Перейдите в консоль Firebase .

  2. В центре страницы обзора проекта нажмите значок Android ( ) или «Добавить приложение» , чтобы запустить рабочий процесс настройки.

  3. Введите имя пакета вашего приложения в поле «Имя пакета Android» .

  4. (Необязательно) Введите другую информацию о приложении: псевдоним приложения и сертификат подписи отладки SHA-1 .

  5. Нажмите «Зарегистрировать приложение» .

Добавьте файл конфигурации Firebase

  1. Загрузите и добавьте файл конфигурации Firebase вашего приложения ( google-services.json ) в вашу кодовую базу:

    1. Нажмите «Загрузить google-services.json» , чтобы получить файл конфигурации Firebase вашего приложения.

    2. Переместите файл конфигурации в корневой каталог модуля (уровня приложения) вашего приложения.

  2. Чтобы сделать значения в файле конфигурации google-services.json доступными для Firebase SDK, вам понадобится плагин Gradle для сервисов Google ( google-services ).

    1. В файле 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.3" 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.3' apply false
      }
    2. В файле 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 в свое приложение

  1. В файле Gradle вашего модуля (уровня приложения) (обычно <project>/<app-module>/build.gradle.kts или <project>/<app-module>/build.gradle ) добавьте зависимость для библиотеки Firebase Cloud Messaging для Android. Мы рекомендуем использовать Firebase Android BoM для управления версиями библиотеки.

    Для оптимальной работы с Firebase Cloud Messaging мы рекомендуем включить Google Analytics в вашем проекте Firebase и добавить Firebase SDK для Google Analytics в ваше приложение.

    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.

    (Альтернатива) Добавьте зависимости библиотеки Firebase без использования BoM

    Если вы решите не использовать 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")
    }
    Ищете модуль библиотеки, специфичный для Kotlin? С октября 2023 года ( Firebase BoM 32.5.0) разработчики как Kotlin, так и Java смогут использовать основной модуль библиотеки (подробности см. в разделе часто задаваемых вопросов об этой инициативе ).

  2. Синхронизируйте свой проект Android с файлами Gradle.

Подпишите клиентское приложение на тему

Клиентские приложения могут подписываться на любую существующую тему или создавать новую. Когда клиентское приложение подписывается на новую тему (которой ещё нет в вашем проекте Firebase), в FCM создаётся новая тема с этим именем, и любой клиент может впоследствии подписаться на неё.

Чтобы подписаться на тему, клиентское приложение вызывает метод Firebase Cloud Messaging subscribeToTopic() с именем темы FCM . Этот метод возвращает Task , которую прослушиватель завершения может использовать для определения успешности подписки:

Kotlin

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 . После этого различные действия ОС, такие как завершение процессов 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

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);
  });

Ява

// 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

В условное выражение можно включить до пяти тем.

Чтобы отправить в состояние:

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);
  });

Ява

// 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

Следующие шаги