web-dev-qa-db-fra.com

Message Firebase avec une priorité élevée ne réveille pas le périphérique de Doze Android 6+

J'ai migré mon projet de GCM vers Firebase. Les notifications Push arrivent ok lorsque le périphérique est réveillé ou récemment endormi, mais si je laisse le périphérique pendant environ une heure, aucun Push n'est envoyé tant que je ne l'ai pas réveillé.

La documentation de Android indique que, si vous devez réactiver un périphérique pour transmettre un message, utilisez FireBase avec une priorité élevée. Il indique également que les applications de l'administrateur de périphérique ne sont pas soumises aux restrictions de Doze. app est une application administrateur de périphérique.

Je pensais mentionner que lorsque j'ai migré le projet de GCM vers FCM, je n'avais spécifié que le nom du package dans la console Firebase et non l'empreinte digitale.

Ce que j'ai essayé.

  1. Définir la priorité sur élevé

    {
      "time_to_live": 300000,
      "delay_while_idle": false,
      "Android": {
        "priority": "high"
      },
      "data": {
        "message": "PING_DEVICE",
        "time": "21/01/2018 16:20:28",
        "pushguid": "10062"
      },
      "registration_ids": [
        "eOMT........"
      ]
    }
    

    Le temps de vivre est défini pour que le message parvienne à terme. delay_while_idle a la valeur false, le FCM l'ignore après septembre 2016.

  2. Les applications d’administration de périphérique ne sont pas soumises à Doze, la mienne est une application d’administration de périphérique, mais j’ai aussi explicitement ajouté l’application à la liste blanche de Doze disponible dans Configuration -> Batterie -> Optimisation. Cela a été fait manuellement via l'application des paramètres et non par programmation dans le code.

J'ai laissé mon appareil dormir pendant 3 heures et aucune poussée ne s'est produite. J'ai également utilisé adb pour mettre l'appareil dans Doze. Quand adb place le périphérique dans Doze, aucun Push n'est reçu, quand adb le supprime de Doze, le Push est transmis.

pensées supplémentaires que je n'ai pas essayées.

Mes push sont des messages de données. En effet, je ne souhaite pas que le Push apparaisse dans la barre de notification du périphérique et demande à l'utilisateur de cliquer dessus pour exécuter la fonctionnalité. L'utilisateur n'a aucune interaction avec l'application d'administration du périphérique. Donc, un message de données est traité par

onMessageReceived(RemoteMessage remoteMessage)

Je crois que les messages de notification réveillent l'appareil, ce dont j'ai besoin, mais je veux que l'application gère le Push, pas l'utilisateur. Pourrais-je avoir des messages qui sont à la fois des notifications et des données mais que l'onMessageRecievied gère la fonctionnalité?

Quelqu'un a-t-il vécu des expériences similaires ou a-t-il des solutions à cela?

[EDIT1] J'ai trouvé le lien ci-dessous indiquant que vous pouvez envoyer un message contenant à la fois une notification et des données. Toutefois, si l'application est en arrière-plan, la notification s'affiche, mais les données ne sont exécutées que lorsque l'utilisateur clique sur la notification. Ce n'est pas ce que je veux car je voudrais que les données s'exécutent immédiatement dans onMessageRecived.

notification avec données

[EDIT2] J'ai ajouté le code et l'autorisation suivants à l'application. L'application demande maintenant à l'utilisateur de la mettre en liste blanche pour Doze. J'ai donc cliqué sur Oui. J'ai ensuite via adb mis le périphérique dans Doze et envoyé un Push. Rien ne s'est passé tant que je n'ai pas retiré l'appareil du mode assoupi. Donc, malheureusement, cela ne fonctionne pas.

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Intent intent = new Intent();
            String packageName = getPackageName();
            PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
            if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + packageName));
                startActivity(intent);
            }
        }

<uses-permission Android:name="Android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

[EDIT3]

J'ai fait d'autres tests pour tenter d'isoler le problème et supprimer le code de mon application Web de l'équation. Je mets le périphérique dans Doze via adb et la console FireBase utilisée pour envoyer le Push à la place. La poussée est correctement passée Cela me dit qu'il y a un problème avec le code de mon application Web qui envoie toutes les informations de la poussée au point final fcm. Je vais obtenir le code ce soir et poster plus tard.

[EDIT4] Je viens de faire quelques tests supplémentaires. J'ai placé l'appareil dans assoupi puis utilisé la console FireBase pour envoyer un message de données avec 2 paires clé-valeur. Lorsque l'appareil est dans Doze et que l'application est au premier plan (à l'écran), le Push s'en va et onMessageReceived s'exécute. C'est bien. Toutefois, si l'application se trouve dans le BG, seule une notification est affichée. Je comprends qu'à partir de la documentation, les messages de données sont distribués à l'activité de lancement via une intention, mais mon application de lancement ne gère pas les envois. La classe qui gère les envois s'appelle MyAndroidFirebaseMsgService et étend FirebaseMessagingService.

Dois-je acheminer l'intention vers cette classe au cas où l'application se trouve dans le BG? Semble un peu starnge pour avoir à faire cela. Ce n’a jamais été le cas dans GCM.

En outre, je ne veux pas que l'application soit lancée à partir d'un Push, car cela est très invasif, l'utilisateur de l'appareil pouvant utiliser une autre application. Mon application est également une application d'administration de périphérique, ce qui signifie que, dans 99% des cas, il n'y a pas d'interaction utilisateur, il ne s'agit que d'un client qui exécute les stratégies sur le périphérique.

[edit5]

internal static void SendNotification (  Dictionary<string, string> nameValues ,  List<string> theregIDs , string sPushName)
         {     
            string stringregIds =  string.Join("\",\"", theregIDs) ;

             JavaScriptSerializer js = new JavaScriptSerializer();
            string keyValueJson = js.Serialize(nameValues);

            string TIME_TO_LIVE = "604800";

            string DELAY_WHILE_IDLE = "false";

            string ENDPOINTADDRESS = @"https://fcm.googleapis.com/fcm/send";


            postData = String.Concat("{\"time_to_live\":", TIME_TO_LIVE,  ",\"delay_while_idle\": ", DELAY_WHILE_IDLE,  ",  \"Android\":{\"priority\":\"high\" } ,\"data\": { \"message\" : " + "\"" + sPushName + "\",\"time\": " + "\"" + System.DateTime.Now.ToString() + "\""
                , keyValueJson
               , "},\"registration_ids\":[\"" + stringregIds + "\"]}");


            WebRequest myWebRequest = null;
            WebResponse myWebResponse = null;
            try
            {
                myWebRequest = WebRequest.Create(ENDPOINTADDRESS);                         
                myWebRequest.Method = "post";
                myWebRequest.ContentType = "application/json";
                //  myWebRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
                myWebRequest.Headers.Add("Authorization: key=" + Our_Api_Key);
                myWebRequest.Headers.Add("Sender:id=" + Our_Sender_Id);

                Byte[] BA = Encoding.UTF8.GetBytes(postData);
                myWebRequest.ContentLength = BA.Length;

                using (Stream dataStreamOut = myWebRequest.GetRequestStream())
                {
                    dataStreamOut.Write(BA, 0, BA.Length);

                }

                using (myWebResponse = myWebRequest.GetResponse())
                {
                    using (Stream dataStream = myWebResponse.GetResponseStream())
                    {
                        using (StreamReader tReader = new StreamReader(dataStream))
                        {
                            strServerResponse = tReader.ReadToEnd(); 
                        }

                    }
                }


            }
            catch (WebException ex)
            {



            }

         }//

merci

29
turtleboy

Après avoir lutté avec un problème similaire, j'ai réussi à le faire fonctionner.

J'envoie les données json suivantes par le facteur:

{
  "data": {
    "body": "Test body from curl"
  },
  "registration_ids": ["Token"],
  "webpush": {
    "headers": {
      "Urgency": "high"
    }
  },
  "Android": {
    "priority": "high"
  },
  "priority": 10
}

Il semble que le dernier "priority":10 est ce qui le fixe pour moi.

Je n'ai trouvé aucune référence à cela dans la documentation de Firebase, mais elle est utilisée dans la documentation désapprouvée de GCM. https://developers.google.com/cloud-messaging/concept-options

5
Patric Eriksson

Vous ne pouvez rien faire.

Il s'agit d'un problème connu causé par une optimisation de la batterie mise en œuvre par certains équipementiers (comme Meizu ou Asus). Lorsqu'une application est balayée dans le commutateur d'applications, l'application est traitée comme si elle était forcée, ce qui n'est pas le comportement par défaut Android. L'inconvénient est que cela peut causer le service FCM pour que votre application cesse de fonctionner. Un effet similaire peut être provoqué sur les messages de priorité élevée en mode assoupi.

L’équipe de Firebase s’efforce d’améliorer ce comportement de son côté, mais le correctif doit venir du côté OEM.

Voici un moyen de vérifier si votre application est affectée par une fonctionnalité de gestion de la batterie d'un OEM:

1) Connectez le périphérique OEM à adb

2) Exécuter votre application sur l'appareil

3) Glissez l'application loin de l'écran récent sur l'appareil.

4) Exécuter la commande: package adb Shell dumpsys MY-PACKAGE | grep arrêté

S'il indique stop = true, vous pouvez en déduire que le fabricant OEM dispose d'un tel mécanisme et que votre application en est affectée.

5
Kuva

Lorsque je travaille sur une application, je suis également bloqué à ce stade. Ensuite, j'ai trouvé un problème à ce sujet sur Github, ce qui a résolu mon problème. C'est,

Sur les appareils fonctionnant sous Android 6.0+, le mode Doze met fin à toutes les connexions en arrière-plan lorsque le téléphone est inactif et n'est pas facturé, y compris la connexion en arrière-plan à Pushy.

Dès que le périphérique est déplacé ou réveillé par l'utilisateur, les connexions en arrière-plan sont restaurées et les notifications en attente sont envoyées en quelques secondes, à condition qu'elles n'aient pas encore expiré.

Pour envoyer des notifications aux appareils en mode Doze, votre application peut déclarer le REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission dans son AndroidManifest.xml et affichez une boîte de dialogue système qui demande aux utilisateurs d’inscrire la liste blanche de votre application parmi les optimisations de batterie sans quitter l’application.

Cela maintiendra effectivement la connexion en arrière-plan à Pushy active et les appareils pourront recevoir des notifications même en mode Doze.

Vous pouvez vérifier ce problème ici https://github.com/ToothlessGear/node-gcm/issues/231

J'espère que ça vous aide!

2
Heisenberg

Il semble qu'il ne soit pas possible de définir une priorité élevée lors de l'envoi d'un champ de données sans notification. Voici la citation de documentation :

Les messages de priorité élevée doivent généralement entraîner une interaction de l'utilisateur avec votre application ou ses notifications. Si FCM détecte un motif dans lequel ils ne le font pas, il est possible que vos messages ne soient plus hiérarchisés.

1
Wojtek

Au lieu d'utiliser "Android": {"priorité": "élevé"}, utilisez comme ceci

    {
      "time_to_live": 300000,
      "delay_while_idle": false,
      "data": {
               "message": "PING_DEVICE",
               "time": "21/01/2018 16:20:28",
               "pushguid": "10062"
               },
      "priority": "high"  
}
0
imodeGowas

Réglage d'un time_to_live sur 0 a résolu le problème pour moi.

Je pense que c'est parce qu'un très petit time_to_live _ dira à la FCM que ce message n’est utile que si le message est transmis immédiatement. Donc, dans le but de le livrer le plus rapidement possible, il ignorera les optimisations de batterie telles que les "compartiments de veille d'application" de Android P). Soyez prudent cependant, car définir un petit time_to_live peut signifier ne pas envoyer la notification du tout. Dans certains cas, je ne pense pas que vous devriez l'appliquer à toutes sortes de notifications Push.

Pour plus d'informations sur time_to_live: https://firebase.google.com/docs/cloud-messaging/concept-options#setting-the-priority-of-a-message

0
user11567387