57 votes

Un message Firebase avec une priorité élevée ne réveille pas l'appareil à partir de la fonction Doze sous Android 6+.

J'ai migré mon projet de l'utilisation de GCM à l'utilisation de Firebase. Les notifications push sont bien envoyées lorsque l'appareil est éveillé ou a été endormi récemment, mais si je laisse l'appareil pendant une heure, aucun push n'est envoyé jusqu'à ce que je réveille l'appareil.

La documentation d'Android indique que si vous devez réveiller un appareil pour lui transmettre un message, utilisez FireBase avec une priorité élevée. Il est également indiqué que les applications d'administration d'appareil ne sont pas soumises aux restrictions de Doze, mon application est une application d'administration d'appareil.

Je pensais mentionner que lorsque j'ai migré le projet de GCM à FCM, j'ai seulement spécifié le nom du paquet dans la console firebase et non l'empreinte digitale.

Ce que j'ai essayé.

  1. Définir la priorité à haute

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

    La durée de vie est fixée de manière à ce que le message finisse par être transmis. delay_while_idle est fixé à false, ce qui est ignoré par le FCM après sept 2016.

  2. Les applications d'administration de l'appareil ne sont pas soumises à Doze. La mienne est une application d'administration de l'appareil mais j'ai également ajouté explicitement l'application à la liste blanche de Doze qui se trouve dans Setting -> Battery -> Optimization. Cela a été fait manuellement par l'intermédiaire de l'application de paramètres et PAS de manière programmatique dans le code.

J'ai laissé mon appareil se mettre en veille pendant 3 heures et aucune poussée ne s'est produite. J'ai également utilisé adb pour mettre l'appareil en mode Doze. Lorsque adb met l'appareil en mode Doze, aucun signal n'est reçu, mais lorsque adb sort l'appareil du mode Doze, le signal est reçu.

d'autres pensées que je n'ai pas essayées.

Mes poussées sont des messages de données. En effet, je ne veux pas que le push arrive dans la barre de notification de l'appareil et que l'utilisateur clique dessus pour exécuter la fonctionnalité. L'utilisateur n'a aucune interaction avec l'application d'administration de l'appareil. Ainsi, un message de données est géré par

onMessageReceived(RemoteMessage remoteMessage)

Je crois que les messages de notification réveillent l'appareil, ce qui est 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 onMessageRecievied gère la fonctionnalité ?

Quelqu'un a-t-il vécu une expérience similaire ou a-t-il une solution à proposer ?

[EDIT1] J'ai trouvé le lien suivant qui dit que vous pouvez envoyer un message qui est à la fois une notification et des données, mais si l'application est en arrière-plan, la notification est affichée 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 soient exécutées directement dans onMessageRecived.

notification avec données

[EDIT2] J'ai ajouté le code et la permission suivants à l'application. L'application demande maintenant à l'utilisateur de mettre l'application sur une liste blanche pour Doze, j'ai donc cliqué sur oui. J'ai ensuite via adb mis l'appareil en Doze et envoyé un push. Rien n'est arrivé jusqu'à ce que je retire l'appareil du mode Doze. 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 effectué d'autres tests pour essayer d'isoler le problème et de retirer le code de mon application web de l'équation. J'ai mis l'appareil en mode Doze via adb et j'ai utilisé la console FireBase pour envoyer le push à la place. Le coup de pouce a été donné correctement Cela me dit qu'il y a un problème avec le code de mon application web qui envoie toutes les informations de poussée au point de terminaison fcm. Je vais récupérer le code ce soir et le poster plus tard.

[EDIT4] Je viens de faire d'autres tests. J'ai placé l'appareil en mode doze puis j'ai utilisé la console FireBase pour envoyer un message de données avec 2 paires clé-valeur. Lorsque l'appareil est en mode Doze et que l'application est au premier plan (sur l'écran), le message est envoyé et onMessageReceived s'exécute. C'est très bien. Cependant, si l'application est au premier plan, seule une notification s'affiche. Je comprends que, d'après la documentation, les messages de données sont envoyés à l'activité du lanceur via un Intent, mais mon application de lanceur ne gère pas les pushs. La classe qui gère les pushs s'appelle MyAndroidFirebaseMsgService et étend FirebaseMessagingService.

Dois-je acheminer l'intention vers cette classe si l'application se trouve dans le BG ? Cela me semble un peu bizarre de devoir faire cela. Cela n'a jamais été le cas dans le GCM.

De plus, je ne veux pas que l'application soit lancée à partir d'un bouton poussoir, car cela est très invasif et l'utilisateur de l'appareil pourrait être en train d'utiliser une autre application. Mon application est également une application d'administration de périphérique, donc 99% du temps il n'y a pas d'interaction avec l'utilisateur, c'est juste un client qui exécute les politiques 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

0 votes

Est-ce que cela fonctionne dans les versions Android inférieures, et où ajoutez-vous la priorité et les données ?

0 votes

@PeterHaddad Bonjour, je ne l'ai pas testé sur les versions inférieures d'Android. Tous les téléphones que je vais acheter seront sous Android 6+. Je vais demander à mon partenaire commercial de me donner un extrait de code de la façon dont il envoie les données au point de terminaison FCM. Y a-t-il un endroit ou un ordre particulier où je dois définir la priorité et les données ?

0 votes

En ce qui concerne votre EDIT3, la console Firebase ne permet d'envoyer que des messages de type "push". notifications (pour autant que je m'en souvienne). Ceux-ci sont gérés par les services Google Play si votre application n'est pas en cours d'exécution qui publient la notification en votre nom. Il ne démarre pas votre application et vous ne recevez pas la notification. onMessageReceived le rappel. Avez-vous reçu ce rappel ?

23voto

Patric Eriksson Points 71

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

J'envoie les données json suivantes par postman :

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

Il semble que la dernière "priority":10 est ce qui le répare pour moi.

Je n'ai pas trouvé de référence à cela dans la documentation de Firebase, mais dans la documentation obsolète de GCM, c'est utilisé. https://developers.google.com/cloud-messaging/concept-options

1 votes

J'ai ajouté webpush, Android et la priorité, maintenant cela fonctionne pour moi sur les appareils Samsung, Motorola et Huawei avec Android 8, lorsque l'appareil est en veille et non connecté au chargeur... Merci de partager cette configuration ! (J'ai utilisé une ancienne dépendance de php, sly/notification-pusher qui utilisait la classe zendframework github.com/zendframework/ZendService_Google_Gcm/blob/master/src/ qui n'avait pas encore le champ de priorité défini dans ma version)

0 votes

J'obtiens l'erreur suivante : Unknown name "priority" at 'message' : Impossible de trouver le champ.

0 votes

@Arjun Je reçois la même erreur. Comment l'avez-vous résolu ?

8voto

Kuva Points 1107

Il n'y a rien que vous puissiez faire.

Il s'agit d'un problème connu qui est causé par une optimisation de la batterie mise en œuvre par certains équipementiers (comme Meizu ou Asus). Lorsqu'une application est déplacée dans le sélecteur d'applications, elle est traitée comme si elle avait été arrêtée de force, ce qui n'est pas le comportement par défaut d'Android. L'effet secondaire malheureux de ceci est que le service FCM de votre application peut s'arrêter de fonctionner. Un effet similaire peut être provoqué sur les messages de haute priorité en mode doze.

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

Une façon de vérifier si votre application est affectée par la fonction de gestion de la batterie d'un OEM est la suivante :

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

2) Exécutez votre application sur l'appareil

3) Faites glisser l'application hors de l'écran récent de l'appareil.

4) Exécutez la commande : adb shell dumpsys package MY-PACKAGE | grep stopped

S'il affiche stopped=true, on peut supposer que l'équipementier dispose d'un tel mécanisme et que votre application est affectée par celui-ci.

0 votes

Bonjour, non il n'est pas arrêté. hero2lte:/ $ dumpsys package devreach.co.uk.devreach | grep stopped User 0 : ceDataInode=397339 installed=true hidden=false suspended=false stopped=false notLaunched=false enabled=0

0 votes

J'ai également une autre application qui a été construite sur une autre machine et qui fonctionne sur le même appareil. C'est un système séparé qui utilise toujours GCM pour délivrer le push. Si je mets l'appareil en mode Doze, le push arrive sans problème. J'ai lu dans d'autres messages que certaines personnes ont mis à jour leur studio Android et leur build gradle et que cela a empêché les pushs de parvenir à l'appareil lorsque celui-ci était en mode Doze.

4voto

HeisenBrg Points 1141

En travaillant sur une application, je suis également bloqué à ce stade. J'ai alors trouvé un problème à ce sujet sur Github, qui a résolu mon problème. C'est à dire,

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 qu'il n'est pas en cours de traitement. connexions en arrière-plan lorsque le téléphone est inactif et n'est pas chargé, y compris la connexion d'arrière-plan à Pushy.

Dès que l'appareil est déplacé ou réveillé par l'utilisateur, l'arrière-plan sont rétablies et toutes les notifications en attente sont en attente seront délivré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 l'adresse REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission dans son AndroidManifest.xml et afficher un dialogue système qui demande aux utilisateurs de de mettre votre application sur la liste blanche des optimisations de la batterie sans quitter l'application.

Cela maintiendra effectivement la connexion en arrière-plan avec 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 cela vous aidera !

0 votes

Bonjour, j'ai déjà mis mon application sur la liste blanche de l'optimisation de la batterie. Dans la question, j'ai indiqué Paramètres -> Batterie -> Optimisation. Est-ce différent de ce que vous suggérez ? De plus, mon application est une application d'administration d'appareil, les docs disent que ces types d'applications sont exempts de Doze.

0 votes

Sur quels appareils mobiles essayez-vous ce programme ?

0 votes

Et je veux juste vérifier, avez-vous ajouté REQUEST_IGNORE_BATTERY_OPTIMIZATIONS dans votre application.

2voto

Wojtek Points 582

Il semble qu'il ne soit pas possible d'accorder une priorité élevée tout en envoyant uniquement le champ de données sans notification. Voici la citation de documentation :

Les messages de haute priorité doivent généralement entraîner une interaction de l'utilisateur avec votre application ou ses notifications. Si la FCM détecte un modèle dans lequel ce n'est pas le cas, vos messages peuvent être dé-priorisés.

0 votes

Est-il possible d'afficher une notification de haute priorité lorsque l'on reçoit un message de haute priorité ?

0 votes

Comme d'habitude, Google suppose que nous voulons tous leur cas d'utilisation par défaut. Personne ne peut avoir une application sensible à la sécurité où une notification de données peut effacer les données de l'utilisateur... ou sauver une vie en obtenant un signal GPS. -.-

0 votes

Salut mon pote, peut-on éviter la dé-priorisation ?

1voto

user11567387 Points 1

Fixer un time_to_live de 0 a résolu le problème pour moi.

Je pense que c'est parce qu'un très petit time_to_live va dire au FCM que ce message ne vaut la peine d'être délivré qu'à l'instant même. Ainsi, pour tenter de le délivrer le plus rapidement possible, il ignorera les optimisations de la batterie telles que les "app standby buckets" d'Android P. Faites attention cependant, car définir un petit temps de vie peut signifier ne pas délivrer la notification du tout dans certains cas. Je ne pense pas que vous devriez l'appliquer à tous les types de notifications push.

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

0 votes

C'est une solution pour toi, pas pour tout le monde, je le pense, as-tu testé sur plusieurs appareils ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X