Com base no modelo publicar/inscrever, as mensagens de tópico do FCM permitem enviar uma mensagem a vários dispositivos que se inscreveram em determinado tópico. Você escreve as mensagens de tópicos conforme necessário e o FCM processa, de maneira confiável, o encaminhamento e a entrega delas aos dispositivos certos.
Por exemplo, os usuários de um app de previsão de marés podem se inscrever em um tópico de "alertas de correntes de maré" e receber notificações das melhores condições de pesca em água salgada em áreas específicas. Os usuários de um app sobre esportes podem se inscrever para receber atualizações automáticas de placares de jogos em tempo real dos times que mais gostam.
Alguns lembretes sobre os tópicos:
- As mensagens de tópicos são mais adequadas a conteúdo de clima ou outras informações disponibilizadas publicamente.
- As mensagens em tópicos são otimizadas para capacidade, não para latência. Para enviar mensagens com rapidez e segurança apenas para um dispositivo ou pequenos grupos de dispositivos, elas devem ser direcionadas para tokens de registro em vez de tópicos.
- Se você precisar enviar mensagens a vários dispositivos por usuário, use as mensagens para grupos de dispositivos.
- As mensagens de tópicos permitem um número ilimitado de assinaturas para cada tópico. No entanto, o FCM
impõe limites nestas áreas:
- Uma instância de app pode estar inscrita em no máximo 2.000 tópicos.
- Se você estiver usando a importação em lote para fazer a inscrição das instâncias de apps, cada solicitação estará limitada a 1.000 instâncias.
- A frequência de novas assinaturas é limitada por projeto. Se forem enviadas muitas
solicitações de assinatura em um curto período, você receberá a seguinte resposta dos servidores do FCM:
429 RESOURCE_EXHAUSTED
("cota excedida"). Tente novamente usando a espera exponencial.
Fazer a inscrição do app cliente em um tópico
Os apps clientes podem ser inscritos em qualquer tópico atual ou podem criar um novo tópico. Quando um app cliente for inscrito em um novo nome de tópico que ainda não existe no seu projeto do Firebase, um novo tópico com esse nome será criado no FCM e qualquer cliente poderá se inscrever nele posteriormente.
Para se inscrever em um tópico, o app cliente chama o subscribeToTopic()
do Firebase Cloud Messaging com o nome do tópico do FCM. Esse método
retorna um Task
, que pode ser usado por um listener de conclusão para determinar se
a inscrição foi bem-sucedida:
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() }
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(); } });
Para cancelar a inscrição, o app cliente chama o unsubscribeFromTopic()
do Firebase Cloud Messaging
com o nome do tópico.
Gerenciar inscrições em tópicos no servidor
Com o Firebase Admin SDK é possível executar tarefas básicas de gerenciamento de tópicos do lado do servidor. Com os tokens de registro, é possível fazer e cancelar a inscrição de instâncias do app clienteem massa usando a lógica do servidor.
É possível inscrever instâncias de apps cliente em qualquer tópico ou criar um novo tópico. Se você usar a API para fazer a inscrição de um app cliente em um novo tópico que ainda não existe no seu projeto do Firebase, um novo tópico com esse nome será criado no FCM e qualquer cliente poderá se inscrever nele depois.
É possível transferir uma lista dos tokens de registro ao método Firebase Admin SDK para inscrever os dispositivos correspondentes em um tópico:
Node.js
// These registration tokens come from the client FCM SDKs.
const registrationTokens = [
'YOUR_REGISTRATION_TOKEN_1',
// ...
'YOUR_REGISTRATION_TOKEN_n'
];
// Subscribe the devices corresponding to the registration tokens to the
// topic.
getMessaging().subscribeToTopic(registrationTokens, topic)
.then((response) => {
// See the MessagingTopicManagementResponse reference documentation
// for the contents of response.
console.log('Successfully subscribed to topic:', response);
})
.catch((error) => {
console.log('Error subscribing to topic:', error);
});
Java
// These registration tokens come from the client FCM SDKs.
List<String> registrationTokens = Arrays.asList(
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n"
);
// Subscribe the devices corresponding to the registration tokens to the
// topic.
TopicManagementResponse response = FirebaseMessaging.getInstance().subscribeToTopic(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " tokens were subscribed successfully");
Python
# These registration tokens come from the client FCM SDKs.
registration_tokens = [
'YOUR_REGISTRATION_TOKEN_1',
# ...
'YOUR_REGISTRATION_TOKEN_n',
]
# Subscribe the devices corresponding to the registration tokens to the
# topic.
response = messaging.subscribe_to_topic(registration_tokens, topic)
# See the TopicManagementResponse reference documentation
# for the contents of response.
print(response.success_count, 'tokens were subscribed successfully')
Go
// These registration tokens come from the client FCM SDKs.
registrationTokens := []string{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
}
// Subscribe the devices corresponding to the registration tokens to the
// topic.
response, err := client.SubscribeToTopic(ctx, registrationTokens, topic)
if err != nil {
log.Fatalln(err)
}
// See the TopicManagementResponse reference documentation
// for the contents of response.
fmt.Println(response.SuccessCount, "tokens were subscribed successfully")
C#
// These registration tokens come from the client FCM SDKs.
var registrationTokens = new List<string>()
{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
};
// Subscribe the devices corresponding to the registration tokens to the
// topic
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
Console.WriteLine($"{response.SuccessCount} tokens were subscribed successfully");
A API Admin do FCM também permite cancelar a inscrição de dispositivos em um tópico passando tokens de registro para o método adequado:
Node.js
// These registration tokens come from the client FCM SDKs.
const registrationTokens = [
'YOUR_REGISTRATION_TOKEN_1',
// ...
'YOUR_REGISTRATION_TOKEN_n'
];
// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
getMessaging().unsubscribeFromTopic(registrationTokens, topic)
.then((response) => {
// See the MessagingTopicManagementResponse reference documentation
// for the contents of response.
console.log('Successfully unsubscribed from topic:', response);
})
.catch((error) => {
console.log('Error unsubscribing from topic:', error);
});
Java
// These registration tokens come from the client FCM SDKs.
List<String> registrationTokens = Arrays.asList(
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n"
);
// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
TopicManagementResponse response = FirebaseMessaging.getInstance().unsubscribeFromTopic(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " tokens were unsubscribed successfully");
Python
# These registration tokens come from the client FCM SDKs.
registration_tokens = [
'YOUR_REGISTRATION_TOKEN_1',
# ...
'YOUR_REGISTRATION_TOKEN_n',
]
# Unubscribe the devices corresponding to the registration tokens from the
# topic.
response = messaging.unsubscribe_from_topic(registration_tokens, topic)
# See the TopicManagementResponse reference documentation
# for the contents of response.
print(response.success_count, 'tokens were unsubscribed successfully')
Go
// These registration tokens come from the client FCM SDKs.
registrationTokens := []string{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
}
// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
response, err := client.UnsubscribeFromTopic(ctx, registrationTokens, topic)
if err != nil {
log.Fatalln(err)
}
// See the TopicManagementResponse reference documentation
// for the contents of response.
fmt.Println(response.SuccessCount, "tokens were unsubscribed successfully")
C#
// These registration tokens come from the client FCM SDKs.
var registrationTokens = new List<string>()
{
"YOUR_REGISTRATION_TOKEN_1",
// ...
"YOUR_REGISTRATION_TOKEN_n",
};
// Unsubscribe the devices corresponding to the registration tokens from the
// topic
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync(
registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
Console.WriteLine($"{response.SuccessCount} tokens were unsubscribed successfully");
Os métodos subscribeToTopic()
e unsubscribeFromTopic()
retornam um objeto
que contém a resposta do FCM. O tipo de retorno tem o mesmo
formato, independentemente do número de tokens de registro especificados na
solicitação.
Em caso de erro (falhas de autenticação, token ou tópico inválido etc.), esses métodos resultam em um erro. Para ver uma lista completa de códigos de erros, incluindo descrições e etapas de solução de problemas, consulte Erros da API Admin do FCM.
Receber e processar mensagens de tópicos
O FCM entrega as mensagens de tópicos da mesma maneira que as outras mensagens downstream.
Para receber mensagens, use um serviço que amplie o
FirebaseMessagingService
.
Seu serviço precisa substituir as callbacks onMessageReceived
e onDeletedMessages
.
A janela de tempo em que a mensagem pode ser processada pode ser menor do que 20 segundos dependendo dos atrasos incorridos antes de chamar onMessageReceived
, incluindo atrasos do SO, o tempo de inicialização do aplicativo, a linha de execução principal bloqueada por outras operações ou onMessageReceived
chamadas estão demorando muito. Depois disso, vários comportamentos do SO, como a eliminação de processos ou os
limites de execução em segundo plano do Android O, podem interferir na capacidade de concluir seu trabalho. do Google Analytics.
O onMessageReceived
está disponível para a maioria dos tipos de mensagens, com as seguintes
exceções:
-
Mensagens de notificação entregues quando seu app estiver em segundo plano. Nesse caso, a notificação é entregue à bandeja do sistema do dispositivo. Quando um usuário toca na notificação, a tela de início do app é aberta por padrão.
-
Mensagens com payload de notificação e dados, quando recebidas em segundo plano. Nesse caso, a notificação é entregue à bandeja do sistema do dispositivo, e o payload de dados é entregue nos extras da intent da atividade da sua tela de início.
Em resumo:
Estado do app | Notificação | Dados | Ambos |
---|---|---|---|
Primeiro plano | onMessageReceived |
onMessageReceived |
onMessageReceived |
Experiência | Bandeja do sistema | onMessageReceived |
Notificação: bandeja do sistema Dados: nos extras da intent. |
Editar o manifesto do app
Para usar FirebaseMessagingService
, adicione o seguinte ao
manifesto do seu app:
<service android:name=".java.MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
Além disso, recomendamos que você defina valores padrão para personalizar a aparência das notificações. É possível especificar uma cor e um ícone padrão personalizados que serão aplicados sempre que valores equivalentes não estiverem definidos no payload da notificação.
Adicione estas linhas na tag
application
para definir a cor e o ícone padrão personalizados:
<!-- 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" />
O Android exibe o ícone personalizado padrão para:
- todas as mensagens de notificação enviadas pelo Editor do Notificações;
- todas as mensagens de notificação que não definem explicitamente o ícone no payload da notificação.
O Android usa a cor padrão personalizada para:
- todas as mensagens de notificação enviadas pelo Editor do Notificações;
- todas as mensagens de notificação que não definem explicitamente a cor no payload de notificação.
Se não houver um ícone personalizado padrão ou nenhum ícone estiver definido no payload de notificação, o Android exibirá o ícone do aplicativo renderizado na cor branca.
Substituir onMessageReceived
Ao substituir o método FirebaseMessagingService.onMessageReceived
,
é possível realizar ações com base no objeto
RemoteMessage
recebido e acessar os dados da mensagem:
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}") // 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. }
Substituir onDeletedMessages
Em algumas situações, é possível que o FCM não entregue uma mensagem. Isso ocorre quando há muitas
mensagens pendentes (mais de 100) no
app em um dispositivo específico no momento em que ele é conectado ou se o dispositivo não for conectado ao
FCM por mais de um mês. Nesses casos,
talvez você receba uma callback para FirebaseMessagingService.onDeletedMessages()
.
Quando isso acontece,
a instância do app precisa executar uma sincronização completa com o servidor do app. Se você não enviou uma mensagem ao app no
dispositivo nas últimas quatro semanas, o FCM não vai chamar o onDeletedMessages()
.
Processar mensagens de notificação em um app em segundo plano
Quando seu app está em segundo plano, o Android direciona as notificações para a bandeja do sistema. Quando um usuário toca nelas, a tela de início do aplicativo é aberta por padrão.
Isso inclui mensagens que contêm payload de notificação e dados, assim como todas aquelas enviadas pelo console do Notificações. Nesses casos, a notificação é entregue à bandeja do sistema do dispositivo e o payload de dados é entregue nos extras da intent da atividade da tela de início.
Confira as informações sobre a entrega de mensagens ao seu app no painel de relatórios do FCM, que registra o número de mensagens enviadas e abertas em dispositivos Apple e Android, além de dados de "impressões" (notificações vistas pelos usuários) para apps Android.
Criar solicitações de envio
Depois de criar um tópico, seja inscrevendo instâncias do app cliente no tópico do lado do cliente ou usando a API do servidor, será possível enviar mensagens ao tópico. Se esta for a primeira vez que você cria solicitações de envio para o FCM, consulte o guia do ambiente do servidor e o FCM para informações gerais importantes e sobre configuração.
Na sua lógica de envio no back-end, especifique o nome do tópico desejado conforme mostrado abaixo:
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);
});
Java
// 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)
Go
// 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);
REST
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"
}
}
}
Comando 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
Para enviar uma mensagem para uma combinação de tópicos,
especifique uma condição, que é uma expressão booleana que especifica os
tópicos de destino. Por exemplo, a seguinte condição vai enviar mensagens para
dispositivos que estão inscritos em TopicA
e TopicB
ou TopicC
:
"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"
No FCM, todas as condições entre parênteses são avaliadas primeiro e depois
expressão é analisada da esquerda para a direita. Na expressão acima, um usuário que se inscreveu em um
só tópico não receberá a mensagem. Do mesmo modo, um usuário que não
se inscreveu em TopicA
também não receberá a mensagem. Somente estas combinações
são válidas:
TopicA
eTopicB
TopicA
eTopicC
Você pode incluir até cinco tópicos na sua expressão condicional.
Para enviar para uma condição:
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);
});
Java
// 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)
Go
// 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);
REST
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",
}
}
}
Comando 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
Próximas etapas
- Para saber mais sobre outras maneiras de enviar mensagens a vários dispositivos, acesse esta página.