具有高优先级的 Firebase 消息不会从 Doze android 6+ 唤醒设备

Posted

技术标签:

【中文标题】具有高优先级的 Firebase 消息不会从 Doze android 6+ 唤醒设备【英文标题】:Firebase message with high priority not waking device from Doze android 6+ 【发布时间】:2018-07-05 05:26:29 【问题描述】:

我已将我的项目从使用 GCM 迁移到使用 Firebase。当设备最近处于唤醒或睡眠状态时,推送通知正常,但如果我离开设备一个小时,则在唤醒设备之前不会发送任何推送。

android 文档说,如果您需要唤醒设备来传递消息,请使用 FireBase,并将优先级设置为高。它还说设备管理应用不受打盹限制,我的应用是设备管理应用。

我想我会提到,当我将项目从 GCM 迁移到 FCM 时,我只在 firebase 控制台中指定了包名,而不是指纹。

我已经尝试过了。

    将优先级设置为高

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

    设定了生存时间,因此消息最终会通过。 delay_while_idle 设置为 false,FCM 会在 2016 年 9 月后忽略它。

    设备管理应用不受 Doze 约束,我的是设备管理应用,但我还明确将该应用添加到设置 -> 电池 -> 优化中的 Doze 白名单中。这是通过设置应用手动完成的,而不是在代码中以编程方式完成。

我已经让我的设备进入睡眠状态 3 小时,并且没有任何推动。我还使用 adb 将设备放入 Doze。当 adb 将设备置于 Doze 时没有收到推送,当 adb 将设备从 Doze 中取出时,推送通过。

我没有尝试过的其他想法。

我的推送是数据消息。这是因为我不希望推送到达设备上的通知栏并让用户单击它来执行功能。用户与设备管理应用程序没有交互。所以一条数据消息由

处理
onMessageReceived(RemoteMessage remoteMessage)

我相信通知消息确实会唤醒设备,这是我需要的,但我希望应用程序而不是用户来处理推送。我能否拥有既是通知又是数据但让 onMessageRecievied 处理功能的消息?

有没有人遇到过类似的情况或有解决办法?

[编辑1] 我发现下面的链接说您可以发送既是通知又是数据的消息,但是如果应用程序在后台,则会显示通知,但仅在用户单击通知时才会执行数据。这不是我想要的,因为我希望数据立即在 onMessageRecived 中执行。

notification with data

[编辑2] 我已向应用程序添加以下代码和权限。该应用程序现在要求用户将该应用程序列入 Doze 应用程序的白名单,因此我单击“是”。然后我通过 adb 将设备置于 Doze 状态并发送推送。直到我将设备退出打盹模式之前,什么都没有发生。所以,不幸的是,这不起作用。

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" />

[编辑3]

我已经进行了进一步的测试,试图找出问题所在,并将我的 Web 应用程序代码排除在外。我通过 adb 将设备放入 Doze 并使用 FireBase 控制台来发送推送。 推送正确完成这告诉我,我的 Web 应用程序代码存在问题,该代码将所有推送信息发送到 fcm 端点。今晚我会得到代码并稍后发布。

[编辑4] 我刚刚做了一些测试。我将设备置于打盹状态,然后使用 FireBase 控制台发送带有 2 个键值对的数据消息。当设备处于打盹且应用程序处于前台(屏幕上)时,推送将通过并执行 onMessageReceived。这很棒。但是,如果应用程序位于 BG 中,则只会显示通知。我从文档中了解到,数据消息通过 Intent 发送到启动器活动,但我的启动器应用程序不处理推送。处理推送的类称为 MyAndroidFirebaseMsgService 并扩展 FirebaseMessagingService。

如果应用程序位于 BG 中,我是否必须将意图路由到此类?似乎有点starnge必须这样做。在 GCM 中从未出现过这种情况。

另外,我不希望应用程序从推送中启动,因为这是非常具有侵入性的,因为设备用户可能正在使用不同的应用程序。我的应用也是一个设备管理应用,所以 99% 的时间没有用户交互,它只是一个在设备上执行策略的客户端。

[编辑5]

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)
            



            

         //

谢谢

【问题讨论】:

它是否适用于较低的android版本,你在哪里添加优先级和数据? @PeterHaddad 嗨,我还没有在较低的 Android 版本上测试过它。我将采购的所有手机都是 Android 6+。我会要求我的业务伙伴给我一个代码 sn-p,说明他如何将数据发送到 FCM 端点。我应该设置优先级和数据的特殊位置或顺序吗? 关于您的 EDIT3,Firebase 控制台只允许发送推送 通知(据我记忆)。这些由 Google Play 服务处理如果您的应用程序未运行,它会代表您发布通知。它不会启动您的应用程序,您也不会收到 onMessageReceived 回调。你收到那个回调了吗? @EugenPechanec 啊,你说得对,它只是一条通知消息,没有产生回调。您对问题可能是什么有任何进一步的想法? 你找到解决办法了吗? 【参考方案1】:

在遇到类似问题后,我设法让它工作。

我通过邮递员发送以下json数据:


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

似乎最后一个"priority":10 是为我修复它的原因。

我在 Firebase 文档中找不到对此的任何引用,但在已弃用的 GCM 文档中使用了它。 https://developers.google.com/cloud-messaging/concept-options

【讨论】:

我添加了 webpush、android 和优先级,现在它适用于我在 android 8 的三星、摩托罗拉和华为设备上,当设备进入睡眠状态并且没有连接到充电器时......谢谢分享这个配置!! (我使用了一个旧的 php 依赖项,sly/notification-pusher,它使用了 zendframework 类 github.com/zendframework/ZendService_Google_Gcm/blob/master/src/… ,我的版本中还没有设置优先级字段) 我收到以下错误:“消息”中的未知名称“优先级”:找不到字段。【参考方案2】:

TL;DR - 确保根据 FCM 的旧 HTTP 协议与其 HTTP v1 协议的 JSON 有效负载结构正确设置通知优先级。

根据您的情况或实现,上面的帖子可能已经有足够的答案,但我想根据 FCM 在其文档中提供的旧 HTTP 和 HTTP v1 协议之间的区别提供更多上下文的答案,但是是设置通知优先级时两个协议 API 之间的细微差别。

我们的团队遇到了同样的问题,即在启用了 Doze 的 Android 6+ 设备上无法接收推送通知,即使我们的服务器似乎在 FCM API 负载中正确设置了与原始问题中提供的负载类似的优先级。我们依靠 Amazon SNS 将负载转发到 FCM,从我们的服务器发送到 Amazon SNS 的负载将根据AndroidConfig JSON 对象设置优先级:


    "android": 
        "priority": "high"
    

但是,这仅根据 HTTP v1 protocol 是正确的。我们没有意识到 Amazon SNS 可能仍在使用 legacy HTTP protocol,其中优先级必须设置为JSON 负载的顶层:


    "priority": "high", // legacy HTTP protocol (this can also be set to 10)
    "android": 
        "priority": "high" // HTTP v1 protocol
    

因此,通知优先级只有在传统 HTTP 优先级参数设置为“高”或 10 时才会生效并允许在打盹时接收推送通知。

对于上下文,这些是向 FCM 发送消息时每个协议的 API 端点:

旧版 HTTP:https://fcm.googleapis.com/fcm/send 需要*** "priority" 参数 (see here) HTTP v1:https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send 需要 "android" 对象和 "priority" 参数 (see here)

【讨论】:

真的,为此苦苦挣扎,您还必须在 android manifest 中启用 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 吗? @Pulkit 我不需要添加那个标志,只需将 legacy 和 v1 都设置为“high”。然后使用 Azure Notifications Hub 进行测试,并在应用“关闭”时收到消息 - Pixel 4 用于测试 谢谢。 fcm http legacy 发送高优先级消息的示例,在顶层设置的 "priority": "high" 对 Android 起到了作用。 "to": "把 fcm 在应用中分配的设备令牌放在这里", "priority": "high", "notification": "title": "把标题放在这里", "body": "放在这里正文”,“数据”:“customkey1”:“customdata1”,“customkey2”:“customdata2”【参考方案3】:

你无能为力。

这是一个已知问题,由某些 OEM(如魅族或华硕)实施的电池优化引起。当应用程序在应用程序切换器中滑动时,应用程序将被视为强制停止,这不是默认的 Android 行为。不幸的副作用是它可能导致您的应用程序的 FCM 服务停止运行。打盹模式下的高优先级消息也会产生类似的效果。

Firebase 团队正在努力从头开始改善这种行为,但实际的修复必须来自 OEM 方面。

检查您的应用是否受到任何 OEM 的电池管理功能影响的一种方法如下:

1) 将 OEM 设备附加到 adb

2) 在设备上运行您的应用

3) 将应用从设备上的最近屏幕上滑开

4) 运行命令:adb shell dumpsys package MY-PACKAGE | grep 停止

如果它显示stopped=true,则可以安全地假设OEM 具有这样的机制并且您的应用也受到了同样的影响。

【讨论】:

嗨,不,它没有停止。 hero2lte:/ $ dumpsys package devreach.co.uk.devreach | grep 停止用户 0: ceDataInode=397339 安装=true hidden=false 暂停=false 停止=false notLaunched=false enabled=0 我还有另一个应用程序,它是在不同的机器上构建的,可以在同一设备上运行。它是一个单独的系统,仍然使用 GCM 来传递推送。如果我将设备放入打瞌睡状态,则推送效果很好。我在其他帖子中读到,有些人已经更新了他们的 Android Studio 和 gradle 构建,这阻止了在打瞌睡时推送到设备。在他们没事之前。所以我不确定问题仍然存在【参考方案4】:

在处理应用程序时,我也被困在这一点上。然后我在 Github 上发现了一个关于它的问题,解决了我的问题。也就是说,

在运行 Android 6.0+ 的设备上,打盹模式会终止所有后台 手机空闲且未充电时的连接,包括 Pushy 的后台连接。

一旦设备被用户移动或唤醒,背景 连接已恢复,任何待处理的通知都将 几秒钟内送达,前提是它们尚未过期。

要在打盹模式下向设备发送通知,您的应用可以声明 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 权限在其 AndroidManifest.xml 并显示一个系统对话框,要求用户 无需离开应用即可将您的应用列入电池优化白名单。

这将有效地保持与 Pushy 的后台连接处于活动状态 即使在打盹模式下,设备也能接收通知。

你可以在这里查看这个问题https://github.com/ToothlessGear/node-gcm/issues/231

希望对你有帮助!

【讨论】:

嗨,我已经将我的应用列入电池优化白名单。在问题中,我说设置-> 电池-> 优化。这与您的建议不同吗?此外,我的应用是设备管理应用,文档说这些类型的应用可以免于打瞌睡。 您在哪些移动设备上尝试此操作? 我只是想交叉检查一下,您是否在您的应用中添加了REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 权限。 我在 S6、S7 和三星 Xcover3 上测试过。所有运行 Android 6+ 不,我没有在应用程序中添加权限,因为我已通过设置应用程序 UI 将应用程序明确添加到白名单中。我可以添加它,但我会在今晚晚些时候回家。【参考方案5】:

在不通知的情况下仅发送数据字段时似乎无法设置高优先级。这是documentation的引述:

高优先级消息通常会导致用户与您的应用或其通知进行交互。如果 FCM 检测到他们没有检测到的模式,您的消息可能会被取消优先级。

【讨论】:

接收高优先级FCM时显示高优先级通知是否可以。 像往常一样,谷歌假设我们都想要他们的默认用例。没有人可以拥有一个安全敏感的应用程序,其中数据通知可以擦除用户数据......或通过获取 GPS 信号来挽救生命。 -.- 嗨,哥们,我们可以避免降低优先级吗?【参考方案6】:

time_to_live 设置为 0 解决了我的问题。

我认为这是因为一个很小的time_to_live 会告诉 FCM,这条消息只值得在这一刻正确传递。因此,为了尽快交付它,它将忽略像 Android P 的“应用程序待机存储桶”这样的电池优化。 不过要小心,因为设置一个小的 time_to_live 可能意味着在某些情况下根本不发送通知。我认为您不应该将其应用于各种推送通知。

更多关于time_to_live的详情:https://firebase.google.com/docs/cloud-messaging/concept-options#setting-the-priority-of-a-message

【讨论】:

这是你的解决方案,不是所有的,我想是的,你测试过很多设备吗?【参考方案7】:

不要使用"android":"priority":"high",而是像这样使用

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

【讨论】:

【参考方案8】:

感谢大家的回复。我们终于对它进行了排序。

我们登录到 Firebase 控制台并意识到由于代码年代久远,我们没有使用控制台中生成的设置/配置文件。对不起,我忘记了它的名字。这个文件有设置等,当推送发送到谷歌时使用。一旦我们在请求中使用了该文件,我的应用程序就可以唤醒打瞌睡的手机。

谢谢

【讨论】:

嗨,我在 fcm 控制台中没有找到你所说的任何东西,你能帮忙吗?

以上是关于具有高优先级的 Firebase 消息不会从 Doze android 6+ 唤醒设备的主要内容,如果未能解决你的问题,请参考以下文章

在 Cloud Function for Firebase 中设置 Firebase 消息的优先级

推送消息无法立即启动服务

具有优先级的消息处理[关闭]

如何将 firebase 错误消息从终端显示到 UI?

收到来自 Firebase 的推送通知 ios 但没有声音或在通知中心

Spring AMQP RabbitMQ 实现优先级队列