web-dev-qa-db-fra.com

Firebase onMessageReceived non appelé lorsque l'application est en arrière-plan

Je travaille avec Firebase et teste l'envoi de notifications à mon application à partir de mon serveur alors que l'application est en arrière-plan. La notification est envoyée avec succès, elle apparaît même sur le centre de notification du périphérique, mais lorsque la notification apparaît ou même si je clique dessus, la méthode onMessageReceived de mon FCMessagingService n'est jamais appelée. 

Lorsque j'ai testé cela alors que mon application était au premier plan, la méthode onMessageReceived a été appelée et tout a bien fonctionné. Le problème se produit lorsque l'application est en cours d'exécution en arrière-plan.

Ce comportement est-il prévu ou y a-t-il un moyen de remédier à cela?

Voici mon 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"));
        }
    }
}
172
Cyogenos

Cela fonctionne comme prévu, les messages de notification sont remis à votre rappel onMessageReceived uniquement lorsque votre application est au premier plan. Si votre application est en arrière-plan ou fermée, un message de notification s'affiche dans le centre de notification et toutes les données de ce message sont transmises à l'intention lancée à la suite du fait que l'utilisateur a appuyé sur la notification.

Vous pouvez spécifier un click_action pour indiquer l'intention à lancer lorsque la notification est exploitée par l'utilisateur. L'activité principale est utilisée si aucune click_action n'est spécifiée.

Lorsque l’intention est lancée, vous pouvez utiliser le

getIntent().getExtras();

pour récupérer un ensemble qui inclurait toutes les données envoyées avec le message de notification.

Pour plus d'informations sur le message de notification, voir docs .

105
Arthur Thompson

Supprimez le champ notificationcomplètement de votre demande de serveur. Envoyez uniquement dataet gérez-le dans onMessageReceived() sinon votre onMessageReceived() ne sera pas déclenché lorsque l'application est en arrière-plan ou est supprimée.

N'oubliez pas d'inclure le champ "priority": "high" dans votre demande de notification. Selon la documentation: les messages de données sont envoyés avec une priorité normale, ils n'arriveront donc pas instantanément; ça pourrait aussi être le problème. 

Voici ce que j'envoie depuis le serveur

{
  "data":{
    "id": 1,
    "missedRequests": 5
    "addAnyDataHere": 123
  },
  "to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......",
  "priority": "high"
}

Ainsi, vous pouvez recevoir vos données dans onMessageReceived(RemoteMessage message) comme ceci .... disons que je dois obtenir un identifiant

Object obj = message.getData().get("id");
        if (obj != null) {
            int id = Integer.valueOf(obj.toString());
        }
74
Zohab Ali

cette méthode handleIntent () a été amortie, vous pouvez donc gérer une notification comme suit:

  1. Etat d'avant-plan: le clic de la notification ira à l'activité de l'intention en attente que vous indiquez lors de la création d'une notification de manière pro-grammaticale, telle qu'elle est généralement créée avec la charge de données de la notification.

  2. Background/Killed State - Ici, le système lui-même crée une notification basée sur la charge de notification. En cliquant sur cette notification, vous accédez à l'activité de lancement de l'application où vous pouvez facilement extraire les données d'intention dans n'importe laquelle de vos méthodes de cycle de vie.

61
manas.abrol

J'ai eu le même problème. Il est plus facile d'utiliser le "message de données" au lieu de la "notification". Le message de données charge toujours la classe onMessageReceived.

Dans cette classe, vous pouvez créer votre propre notification avec le constructeur de notifications.

Exemple:

 @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());
    }
26
Koot

Voici des concepts plus clairs sur le message firebase. Je l'ai trouvé de leur équipe de soutien.

Firebase a trois types de messages:

Messages de notification: le message de notification fonctionne en arrière-plan ou en avant-plan. Lorsque l'application est en arrière-plan, les messages de notification sont remis à la barre d'état système. Si l'application est au premier plan, les messages sont traités par des rappels onMessageReceived() ou didReceiveRemoteNotification. Ce sont essentiellement ce que l’on appelle les messages d’affichage.

Messages de données: sur la plate-forme Android, les messages de données peuvent fonctionner à l'arrière-plan et à l'avant-plan. Le message de données sera traité par onMessageReceived (). Voici une note spécifique à la plate-forme: Sous Android, la charge de données peut être récupérée dans l’intention utilisée pour lancer votre activité. Pour élaborer, si vous avez "click_action":"launch_Activity_1", vous pouvez récupérer cette intention via getIntent() à partir de Activity_1 uniquement.

Messages contenant à la fois des charges de notification et des données: en arrière-plan, les applications reçoivent la charge de notification dans la barre de notification et ne gèrent la charge de données que lorsque l'utilisateur appuie sur la notification. Au premier plan, votre application reçoit un objet de message avec les deux charges utiles disponibles. Deuxièmement, le paramètre click_action est souvent utilisé dans la charge de notification et non dans la charge de données. S'il est utilisé dans les données, ce paramètre est traité comme une paire clé-valeur personnalisée. Par conséquent, vous devez implémenter une logique personnalisée pour que cela fonctionne comme prévu.

De plus, je vous recommande d'utiliser la méthode onMessageReceived (voir Message de données) pour extraire le paquet de données. A partir de votre logique, j'ai vérifié l'objet bundle et je n'ai pas trouvé le contenu de données attendu. Voici une référence à un cas similaire qui pourrait fournir plus de clarté.

Pour plus d'informations, visitez mon ce fil de discussion

21
Md. Sajedul Karim

Selon la documentation de Firebase Cloud Messaging: si Activity est au premier plan, onMessageReceived sera appelé. Si Activity est en arrière-plan ou fermé, un message de notification s'affiche dans le centre de notification pour l'activité du lanceur d'applications . Vous pouvez appeler votre activité personnalisée en cliquant sur notification si votre application est en arrière-plan.

URL - https://fcm.googleapis.com/fcm/send

Type de méthode - POST

Header- Content-Type:application/json
Authorization:key=your api key

Corps/charge utile:

{ "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)"
} 

Et avec cela dans votre application, vous pouvez ajouter le code ci-dessous dans votre activité pour être appelé:

<intent-filter>
                <action Android:name="OPEN_ACTIVITY_1" />
                <category Android:name="Android.intent.category.DEFAULT" />
            </intent-filter>
20
Ankit Adlakha

Si app est en mode arrière-plan ou inactif (tué), et vous cliquez sur sur Notification , vous devez vérifier la charge utile dans LaunchScreen (dans mon cas, l'écran de lancement est MainActivity.Java).

Donc, dans MainActivity.Java on onCreate recherchez Extras :

    if (getIntent().getExtras() != null) {
        for (String key : getIntent().getExtras().keySet()) {
            Object value = getIntent().getExtras().get(key);
            Log.d("MainActivity: ", "Key: " + key + " Value: " + value);
        }
    }
11
Gent Berani

Remplacer la méthode handleIntent de la FirebaseMessageService fonctionne pour moi.

ici le code 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);
    }
}

et c'est le code dans 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);
    }
}
8
t3h Exi

J'ai le même problème. Si l'application est au premier plan, elle déclenche mon service d'arrière-plan et me permet de mettre à jour ma base de données en fonction du type de notification . . 

Voici ma solution pour identifier l'application en arrière-plan et déclencher votre service en arrière-plan,

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

Dans le fichier manifest.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>

Testé cette solution dans la dernière version Android 8.0. Merci

8
Nagendra Badiganti

Par défaut, l’activité Launcher Activity de votre application est lancée lorsque celle-ci est à l’arrière-plan et que vous cliquez sur la notification. Si vous avez des données avec votre notification, vous pouvez les gérer dans la même activité comme suit:

if(getIntent().getExtras()! = null){
  //do your stuff
}else{
  //do that you normally do
}
5
Uzair

Si l'application est en arrière-plan Fire-base en gérant la notification par défaut Mais si nous voulons notre notification personnalisée, nous devons modifier notre côté serveur, qui est responsable de l'envoi de nos données personnalisées (charge utile de données)

Supprimez complètement la charge de notification de votre demande de serveur. Envoyez uniquement des données et gérez-les dans onMessageReceived (), sinon votre onMessageReceived ne sera pas déclenché lorsque l'application est en arrière-plan ou supprimée.

maintenant, votre format de code côté serveur ressemble à,

{
  "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
  }
}

NOTE: voir cette ligne dans le code ci-dessus
"text": "John Doe a partagé un contact dans le groupe Exchange de contact" Dans la charge utile de données, vous devez utiliser le paramètre "text" au lieu des paramètres "body" ou "message" pour la description du message utiliser du texte.

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());
            }
        }
    }
3
Hiren

onMessageReceived (RemoteMessage remoteMessage) méthode appelée en fonction des cas suivants.

  • Réponse FCM avec notification et bloc de données :

{" to ": "liste de jetons de périphérique", " notification " : {"Body": "Corps de votre notification", "title": "Titre de votre notification"}, " data ": {"body ":" Corps de votre notification dans les données "," titre ":" Titre de votre notification dans le titre "," clé_1 ":" Valeur de la clé_1 "," image_url ":" www.abc.com/xyz.jpeg ", "key_2": "Valeur pour key_2"}}

  1. App au premier plan:

onMessageReceived (RemoteMessage remoteMessage) appelé, affiche LargeIcon et BigPicture dans la barre de notification. Nous pouvons lire le contenu des blocs de notification et

  1. App en arrière-plan:

onMessageReceived (RemoteMessage remoteMessage) non appelée, la barre d'état système recevra le message et lira le corps et le titre de la notification bloque et affiche le message et le titre par défaut dans la barre de notification.

  • Réponse FCM avec uniquement bloc de données :

Dans ce cas, supprimer les blocs de notification de json

{" à ": "liste de jetons de périphérique", " data " : {"Body": "Corps de votre notification dans les données", "title": "Titre de votre notification dans le titre", "key_1": "Valeur de key_1", "image_url": "www.abc.com/xyz .jpeg "," key_2 ":" Valeur de key_2 "}}

  1. App au premier plan:

onMessageReceived (RemoteMessage remoteMessage) appelé, affiche LargeIcon et BigPicture dans la barre de notification. Nous pouvons lire le contenu des blocs de notification et

  1. App en arrière-plan:

onMessageReceived (RemoteMessage remoteMessage) appelé, la barre d'état système ne recevra pas le message en raison de la notification la clé n'est pas dans la réponse. Affiche LargeIcon et BigPicture dans la barre de notification

Code

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


}

Lien de référence:

https://firebase.google.com/docs/cloud-messaging/Android/receive

3
vishnuc156

selon la solution de t3h Exi je voudrais poster le code propre ici. Il suffit de le mettre dans MyFirebaseMessagingService et tout fonctionne correctement si l'application est en mode arrière-plan. Vous devez au moins compiler com.google.firebase: firebase-messaging: 10.2.1

 @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);
    }
}
2
Frank

Appelez simplement ceci dans la méthode onCreate de votre MainActivity: 

if (getIntent().getExtras() != null) {
           // Call your NotificationActivity here..
            Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
            startActivity(intent);
        }
2
Shekhar

Essaye ça:

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

Le point qui mérite d'être souligné est que vous devez utiliser le message de données - clé uniquement - pour obtenir le gestionnaire onMessageReceived appelé même lorsque l'application est en arrière-plan. Vous ne devriez avoir aucune autre clé de message de notification dans votre charge utile, sinon le gestionnaire ne sera pas déclenché si l'application est en arrière-plan.

Il est mentionné (mais pas autant souligné dans la documentation de la FCM) ici:

https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages

Utilisez votre serveur d'applications et l'API du serveur FCM: définissez la clé data uniquement. Peut être soit pliable soit non pliable.

1
n_y

Si votre problème est lié à l’affichage de Big Image, c’est-à-dire si vous envoyez une notification Push avec une image à partir de la console firebase, elle n’affiche cette image que si l’application est au premier plan. La solution à ce problème consiste à envoyer un message Push avec un seul champ de données. Quelque chose comme ça:

{ "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" }
1
Arun

Le backend avec lequel je travaille utilise Notification messages et non des messages de données. Ainsi, après avoir lu toutes les réponses, j'ai essayé d'extraire les extras du paquet d'intention lié à l'activité lancée . Mais peu importe les clés que j'ai essayé d'extraire de getIntent().getExtras();, la valeur était toujours nulle. 

Cependant, j'ai finalement trouvé un moyen d'envoyer des données en utilisant Notification messages et de les récupérer à partir de l'intention. 

La clé ici est d'ajouter le data payload au message Notification. 

Exemple:

{
    "data": {
        "message": "message_body",
        "title": "message_title"
    },
    "notification": {
        "body": "test body",
        "title": "test title"
    },
    "to": "E4An.."
}

Après cela, vous pourrez obtenir vos informations de cette manière:

intent.getExtras().getString("title") Sera message_title

et intent.getExtras().getString("message") sera message_body

Référence

1
Vito Valov

J'ai eu ce problème (l'application ne veut pas s'ouvrir sur notification cliquez si l'application est en arrière-plan ou fermée), et le problème était un click_action invalide dans le corps de la notification, essayez de le supprimer ou de le remplacer par un élément valide.

1
Octavian Lari

Lorsque le message est reçu et que votre application est en arrière-plan, la notification est envoyée à l'intention des extras de l'activité principale.

Vous pouvez vérifier la valeur supplémentaire dans la fonction oncreate () ou onresume () de l'activité principale.

Vous pouvez vérifier les champs tels que data, table, etc. (celui spécifié dans la notification)

par exemple j'ai envoyé en utilisant des données comme clé

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

}
0
Sanjeev S

Il y a 2 types de Firebase Notifications push:

1- Message de notification (message affiché) -> -- 1.1 Si vous choisissez cette variante, le système d'exploitation le créera lui-même comme une notification si l'application se trouve dans Background et transmettra les données dans la intent. Ensuite, il appartient au client de gérer ces données.

- 1.2 Si l'application est dans Foreground, la notification la recevant via callback-function dans FirebaseMessagingService sera laissée au client.

2- Messages de données (jusqu'à 4 000 données) -> Ces messages sont utilisés pour envoyer uniquement des données au client (en mode silencieux) et il appartient au client de les gérer pour les deux cas, fond/premier plan via la fonction de rappel dans FirebaseMessagingService

Ceci est conforme aux documents officiels: https://firebase.google.com/docs/cloud-messaging/concept-options

0
Balflear

Il existe deux types de messages: les messages de notification et les messages de données . Si vous envoyez uniquement des messages de données, l’objet sans notification est indiqué dans votre chaîne de message. Il serait invoqué lorsque votre application en arrière-plan.

0
Shongsu