notificaciones - push firebase android
Firebase onMessageReceived no se llama cuando la aplicación está en segundo plano (24)
Estoy trabajando con Firebase y probando el envío de notificaciones a mi aplicación desde mi servidor mientras la aplicación está en segundo plano. La notificación se envía con éxito, incluso aparece en el centro de notificaciones del dispositivo, pero cuando aparece la notificación o incluso si hago clic en ella, nunca se llama al método onMessageReceived dentro de mi FCMessagingService.
Cuando probé esto mientras mi aplicación estaba en primer plano, se llamó al método onMessageReceived y todo funcionó bien. El problema ocurre cuando la aplicación se ejecuta en segundo plano.
¿Es este el comportamiento previsto o hay alguna forma de que pueda solucionarlo?
Aquí está mi FBMessagingService:
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class FBMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.i("PVL", "MESSAGE RECEIVED!!");
if (remoteMessage.getNotification().getBody() != null) {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getNotification().getBody());
} else {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getData().get("message"));
}
}
}
Anular el método
handleIntent
del
FirebaseMessageService
funciona para mí.
aquí el código en C # (Xamarin)
public override void HandleIntent(Intent intent)
{
try
{
if (intent.Extras != null)
{
var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
foreach (string key in intent.Extras.KeySet())
{
builder.AddData(key, intent.Extras.Get(key).ToString());
}
this.OnMessageReceived(builder.Build());
}
else
{
base.HandleIntent(intent);
}
}
catch (Exception)
{
base.HandleIntent(intent);
}
}
y ese es el Código en Java
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
Aquí hay conceptos más claros sobre el mensaje de Firebase. Lo encontré de su equipo de soporte.
Firebase tiene tres tipos de mensajes :
Mensajes de notificación
: el mensaje de notificación funciona en segundo plano o en primer plano.
Cuando la aplicación está en segundo plano, los mensajes de notificación se entregan a la bandeja del sistema.
Si la aplicación está en primer plano, los mensajes son manejados por
onMessageReceived()
o
didReceiveRemoteNotification
callbacks.
Estos son esencialmente lo que se denomina Mostrar mensajes.
Mensajes de datos
: en la plataforma Android, los mensajes de datos pueden funcionar en segundo plano y en primer plano.
El mensaje de datos será manejado por onMessageReceived ().
Una nota específica de la plataforma aquí sería: en Android, la carga útil de datos se puede recuperar en la intención utilizada para iniciar su actividad.
Para elaborar, si tiene
"click_action":"launch_Activity_1"
, puede recuperar esta intención a través de
getIntent()
de solo
Activity_1
.
Mensajes con notificaciones y cargas de datos : cuando están en segundo plano, las aplicaciones reciben la carga de notificaciones en la bandeja de notificaciones y solo manejan la carga de datos cuando el usuario toca la notificación. Cuando está en primer plano, su aplicación recibe un objeto de mensaje con ambas cargas disponibles. En segundo lugar, el parámetro click_action se usa a menudo en la carga de notificación y no en la carga de datos. Si se usa dentro de la carga útil de datos, este parámetro se trataría como un par clave-valor personalizado y, por lo tanto, necesitaría implementar una lógica personalizada para que funcione según lo previsto.
Además, le recomiendo que use el método onMessageReceived (consulte Mensaje de datos) para extraer el paquete de datos. Desde su lógica, revisé el objeto del paquete y no he encontrado el contenido de datos esperado. Aquí hay una referencia a un caso similar que podría proporcionar más claridad.
Para más información visite mi este hilo y este hilo
Cuando se recibe un mensaje y su aplicación está en segundo plano, la notificación se envía a la intención adicional de la actividad principal.
Puede verificar el valor adicional en la función oncreate () u onresume () de la actividad principal.
Puede verificar los campos como datos, tabla, etc. (el especificado en la notificación)
por ejemplo envié usando datos como clave
public void onResume(){
super.onResume();
if (getIntent().getStringExtra("data")!=null){
fromnotification=true;
Intent i = new Intent(MainActivity.this, Activity2.class);
i.putExtra("notification","notification");
startActivity(i);
}
}
De acuerdo con la solución de t3h Exi, me gustaría publicar el código limpio aquí. Simplemente colóquelo en MyFirebaseMessagingService y todo funcionará bien si la aplicación está en modo de fondo. Necesita al menos compilar com.google.firebase: firebase-messaging: 10.2.1
public void handleIntent(Intent intent) {
try {
if (intent.getExtras() != null) {
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet()) {
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
} else {
super.handleIntent(intent);
}
} catch (Exception e) {
super.handleIntent(intent);
}
}
De forma predeterminada, la Actividad de inicio en su aplicación se iniciará cuando su aplicación esté en segundo plano y haga clic en la notificación; si tiene alguna parte de datos con su notificación, puede manejarla en la misma actividad de la siguiente manera,
if(getIntent().getExtras()! = null){
//do your stuff
}else{
//do that you normally do
}
El backend con el que estoy trabajando está usando
mensajes de notificación
y no mensajes de datos.
Entonces, después de leer todas las respuestas, traté de recuperar los extras del paquete de la intención que viene con la actividad lanzada.
Pero no importa qué claves intenté recuperar de
getIntent().getExtras();
, el valor siempre fue nulo.
Sin embargo, finalmente encontré una manera de enviar datos utilizando mensajes de notificación y recuperarlos de la intención.
La clave aquí es agregar la carga útil de datos al mensaje de notificación.
Ejemplo:
{
"data": {
"message": "message_body",
"title": "message_title"
},
"notification": {
"body": "test body",
"title": "test title"
},
"to": "E4An.."
}
Después de hacer esto, podrá obtener su información de esta manera:
intent.getExtras().getString("title")
será
message_title
e
intent.getExtras().getString("message")
será
message_body
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
El punto que merece destacarse es que debe usar el mensaje de datos, solo la clave de datos , para obtener una llamada al manejador de mensajes recibidos incluso cuando la aplicación está en segundo plano. No debería tener ninguna otra clave de mensaje de notificación en su carga útil, de lo contrario, el controlador no se activará si la aplicación está en segundo plano.
Aquí se menciona (pero no se enfatiza tanto en la documentación de FCM):
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
Use su servidor de aplicaciones y la API del servidor FCM: configure solo la clave de datos . Puede ser plegable o no plegable.
Estaba teniendo el mismo problema e investigé un poco más sobre esto.
Cuando la aplicación está en segundo plano, se envía un
mensaje de notificación
a la bandeja del sistema, PERO se envía un
mensaje de datos
a
onMessageReceived()
Ver
https://firebase.google.com/docs/cloud-messaging/downstream#monitor-token-generation_3
y
https://github.com/firebase/quickstart-android/blob/master/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/MyFirebaseMessagingService.java
Para asegurarse de que el mensaje que está enviando, los documentos dicen: "
Use su servidor de aplicaciones y la API del servidor FCM: configure la clave de datos solamente. Puede ser plegable o no plegable
"
.
Ver
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
Esto funciona según lo previsto, los mensajes de notificación se envían a su devolución de llamada onMessageReceived solo cuando su aplicación está en primer plano. Si su aplicación está en segundo plano o cerrada, se muestra un mensaje de notificación en el centro de notificaciones, y cualquier dato de ese mensaje se pasa a la intención que se inicia como resultado de que el usuario toque la notificación.
Puede especificar una acción click_ para indicar la intención que debe iniciarse cuando el usuario toca la notificación. La actividad principal se usa si no se especifica click_action.
Cuando se inicia la intención, puede usar el
getIntent().getExtras();
para recuperar un conjunto que incluiría cualquier dato enviado junto con el mensaje de notificación.
Para más información sobre el mensaje de notificación, consulte los docs .
Hay 2 tipos de notificaciones push de Firebase :
1- Mensaje de notificación (Mostrar mensaje) -> - 1.1 Si elige esta variante, el sistema operativo creará una notificación si la aplicación está en
segundo plano
y pasará los datos en la
intent
.
Entonces depende del cliente manejar estos datos.
- 1.2 Si la aplicación está en
primer plano
, la notificación se recibirá a través
callback-function
en
FirebaseMessagingService
y depende del cliente manejarla.
2- Mensajes de datos (hasta 4k datos) -> Estos mensajes se usan para enviar solo datos al cliente (en silencio) y depende del cliente manejarlos en ambos casos en segundo plano / fondo a través de la función de devolución de llamada en
FirebaseMessagingService
Esto está de acuerdo con los documentos oficiales: https://firebase.google.com/docs/cloud-messaging/concept-options
Hay dos tipos de mensajes: mensajes de notificación y mensajes de datos. Si solo envía un mensaje de datos, es decir, sin objeto de notificación en su cadena de mensaje. Se invocará cuando su aplicación esté en segundo plano.
Prueba esto:
private void sendNotification(Bitmap bitmap, String title, String
message, PendingIntent resultPendingIntent) {
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
style.bigPicture(bitmap);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
String NOTIFICATION_CHANNEL_ID = mContext.getString(R.string.default_notification_channel_id);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(notificationChannel);
}
Bitmap iconLarge = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.mdmlogo);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.mdmlogo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSound)
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setStyle(style)
.setLargeIcon(iconLarge)
.setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_MAX)
.setChannelId(NOTIFICATION_CHANNEL_ID);
notificationManager.notify(1, notificationBuilder.build());
}
Según la documentación de Firebase Cloud Messaging: si la actividad está en primer plano, se llamará a onMessageReceived. Si la actividad está en segundo plano o cerrada, se muestra un mensaje de notificación en el centro de notificaciones para la actividad del iniciador de aplicaciones. Puede llamar a su actividad personalizada al hacer clic en la notificación si su aplicación está en segundo plano llamando a la API del servicio de descanso para mensajes de Firebase como:
URL- https://fcm.googleapis.com/fcm/send
Tipo de método: POST
Header- Content-Type:application/json
Authorization:key=your api key
Cuerpo / carga útil:
{ "notification": {
"title": "Your Title",
"text": "Your Text",
"click_action": "OPEN_ACTIVITY_1" // should match to your intent filter
},
"data": {
"keyname": "any value " //you can get this data as extras in your activity and this data is optional
},
"to" : "to_id(firebase refreshedToken)"
}
Y con esto en su aplicación, puede agregar el siguiente código en su actividad que se llamará:
<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Si la aplicación está en modo de fondo o inactiva (inactiva) y hace clic en Notificación , debe verificar la carga útil en LaunchScreen (en mi caso, la pantalla de inicio es MainActivity.java).
Entonces, en MainActivity.java en onCreate verifique Extras :
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
Object value = getIntent().getExtras().get(key);
Log.d("MainActivity: ", "Key: " + key + " Value: " + value);
}
}
Si la aplicación está en segundo plano Fire-base de forma predeterminada notificación de manejo Pero si queremos nuestra notificación personalizada, entonces tenemos que cambiar nuestro lado del servidor, que es responsable de enviar nuestros datos personalizados (carga de datos)
Elimine la carga de notificación por completo de su solicitud de servidor. Enviar solo datos y manejarlos en onMessageReceived () de lo contrario, su onMessageReceived no se activará cuando la aplicación esté en segundo plano o se cierre.
ahora, el formato del código del lado del servidor se ve así,
{
"collapse_key": "CHAT_MESSAGE_CONTACT",
"data": {
"loc_key": "CHAT_MESSAGE_CONTACT",
"loc_args": ["John Doe", "Contact Exchange"],
"text": "John Doe shared a contact in the group Contact Exchange",
"custom": {
"chat_id": 241233,
"msg_id": 123
},
"badge": 1,
"sound": "sound1.mp3",
"mute": true
}
}
NOTA
: vea esta línea en el código anterior
"texto": "John Doe compartió un contacto en el grupo Intercambio de contactos" en la carga de datos, debe usar el parámetro "texto" en lugar de los parámetros "cuerpo" o "mensaje" para la descripción del mensaje o lo que quiera usar texto.
onMessageReceived ()
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "From: " + remoteMessage.getData().toString());
if (remoteMessage == null)
return;
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
/* Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());*/
Log.e(TAG, "Data Payload: " + remoteMessage);
try {
Map<String, String> params = remoteMessage.getData();
JSONObject json = new JSONObject(params);
Log.e("JSON_OBJECT", json.toString());
Log.e(TAG, "onMessageReceived: " + json.toString());
handleDataMessage(json);
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
}
Si su problema está relacionado con mostrar Big Image, es decir, si está enviando notificaciones push con una imagen desde la consola de Firebase y muestra la imagen solo si la aplicación está en primer plano. La solución para este problema es enviar un mensaje push con solo un campo de datos. Algo como esto:
{ "data": { "image": "https://static.pexels.com/photos/4825/red-love-romantic-flowers.jpg", "message": "Firebase Push Message Using API" "AnotherActivity": "True" }, "to" : "device id Or Device token" }
Simplemente anule el método OnCreate de FirebaseMessagingService. Se llama cuando su aplicación está en segundo plano:
public override void OnCreate()
{
// your code
base.OnCreate();
}
Simplemente llame a esto en el método onCreate de su MainActivity:
@Override
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
Tengo el mismo problema. Si la aplicación está en primer plano, activa mi servicio en segundo plano donde puedo actualizar mi base de datos en función del tipo de notificación. Pero, la aplicación pasa a segundo plano: el servicio de notificación predeterminado se encargará de mostrar la notificación al usuario.
Aquí está mi solución para identificar la aplicación en segundo plano y activar su servicio en segundo plano,
public class FirebaseBackgroundService extends WakefulBroadcastReceiver {
private static final String TAG = "FirebaseService";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "I''m in!!!");
if (intent.getExtras() != null) {
for (String key : intent.getExtras().keySet()) {
Object value = intent.getExtras().get(key);
Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
if(key.equalsIgnoreCase("gcm.notification.body") && value != null) {
Bundle bundle = new Bundle();
Intent backgroundIntent = new Intent(context, BackgroundSyncJobService.class);
bundle.putString("push_message", value + "");
backgroundIntent.putExtras(bundle);
context.startService(backgroundIntent);
}
}
}
}
}
En el manifiesto.xml
<receiver android:exported="true" android:name=".FirebaseBackgroundService" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
Probamos esta solución en la última versión de Android 8.0. Gracias
Tuve este problema (la aplicación no quiere abrirse en el clic de notificación si la aplicación está en segundo plano o cerrada), y el problema fue una
click_action
de
click_action
no
click_action
en el cuerpo de la notificación, intente eliminarlo o cambiarlo a algo válido.
Yo tuve el mismo problema. Es más fácil usar el ''mensaje de datos'' en lugar de la ''notificación''. El mensaje de datos siempre carga la clase onMessageReceived.
En esa clase, puede hacer su propia notificación con el generador de notificaciones.
Ejemplo:
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
sendNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"));
}
private void sendNotification(String messageTitle,String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0 /* request code */, intent,PendingIntent.FLAG_UPDATE_CURRENT);
long[] pattern = {500,500,500,500,500};
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(true)
.setVibrate(pattern)
.setLights(Color.BLUE,1,1)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
este método handleIntent () se ha depreciado, por lo que el manejo de una notificación se puede hacer de la siguiente manera:
-
Estado de primer plano: el clic de la notificación irá a la actividad del Intento pendiente que está proporcionando al crear una notificación de forma programática, ya que generalmente se crea con la carga útil de datos de la notificación.
-
Antecedentes / Estado eliminado: aquí, el propio sistema crea una notificación basada en la carga útil de la notificación y al hacer clic en esa notificación lo llevará a la actividad del iniciador de la aplicación, donde puede obtener fácilmente datos de Intención en cualquiera de sus métodos de ciclo de vida.
Elimine el campo de
notification
completamente de su solicitud del servidor.
Envíe
solo
data
y
onMessageReceived()
en
onMessageReceived()
contrario, su
onMessageReceived()
no se activará cuando la aplicación esté en segundo plano o se cierre.
No olvide incluir
"priority": "high"
campo
"priority": "high"
en su solicitud de notificación.
Según la documentación: los mensajes de datos se envían con una prioridad normal, por lo que no llegarán instantáneamente;
También podría ser el problema.
Esto es lo que estoy enviando desde el servidor
{
"data":{
"id": 1,
"missedRequests": 5
"addAnyDataHere": 123
},
"to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......",
"priority": "high"
}
Para que pueda recibir sus datos en
onMessageReceived(RemoteMessage message)
esta manera ... digamos que tengo que obtener la identificación
Object obj = message.getData().get("id");
if (obj != null) {
int id = Integer.valueOf(obj.toString());
}
Método onMessageReceived (RemoteMessage remoteMessage) llamado en función de los siguientes casos.
- Respuesta FCM con notificación y bloqueo de datos :
{" to ": "lista de tokens del dispositivo", " notificación ": {"body": "Body of Your Notification", "title": "Title of Your Notification"}, " data ": {"body": "Body de su notificación en los datos "," title ":" Título de su notificación en el título "," key_1 ":" Value for key_1 "," image_url ":" www.abc.com/xyz.jpeg "," key_2 ": "Valor para clave_2"}}
- Aplicación en primer plano:
onMessageReceived (RemoteMessage remoteMessage) llamado, muestra LargeIcon y BigPicture en la barra de notificaciones. Podemos leer el contenido tanto de la notificación como del bloque de datos
- Aplicación en segundo plano:
onMessageReceived (RemoteMessage remoteMessage) no llamado, la bandeja del sistema recibirá el mensaje y leerá el cuerpo y el título del bloque de notificaciones y muestra el mensaje y el título predeterminados en la barra de notificaciones.
- Respuesta FCM con solo bloque de datos :
En este caso, eliminar bloques de notofocación de json
{" to ": "lista de tokens del dispositivo", " data ": {"body": "Cuerpo de su notificación en los datos", "title": "Título de su notificación en el título", "key_1": "Valor para key_1 "," image_url ":" www.abc.com/xyz.jpeg "," key_2 ":" Valor para key_2 "}}
- Aplicación en primer plano:
onMessageReceived (RemoteMessage remoteMessage) llamado, muestra LargeIcon y BigPicture en la barra de notificaciones. Podemos leer el contenido tanto de la notificación como del bloque de datos
- Aplicación en segundo plano:
onMessageReceived (RemoteMessage remoteMessage) llamado, la bandeja del sistema no recibirá el mensaje porque la clave de notificación no está en la respuesta. Muestra LargeIcon y BigPicture en la barra de notificaciones
Código
if (getIntent().getExtras() != null) {
// Call your NotificationActivity here..
Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
startActivity(intent);
}
Link de referencia:
https://firebase.google.com/docs/cloud-messaging/android/receive