【问题标题】:Notifications not received on Android在 Android 上未收到通知
【发布时间】:2018-05-03 09:29:04
【问题描述】:

我正在开发一个 android 应用程序,其主要用途是在设定的时间显示通知(某些特定的日历应用程序)。主要抱怨之一是用户不会(总是)收到通知,我束手无策。

我们在模拟器上针对 Android 4.4、6、7.0、7.1、8.0、8.1 对以下代码进行了内部测试,并使用了大约 10 台真实设备(6 到 8.1),所有设备都按时收到了通知。即使在重新启动后,通知也都按时收到了。

我们遇到的其中一件事情是三星设备上的 SecurityException(>500 个已注册警报),我们之前因取消不当而触发了该异常。看起来这不再是问题了。

那么,这些丢失通知的原因可能是什么?这是设备特定的设置,还是一个简单的错误?还是有其他因素在起作用?

这是我们正在使用的代码:

private void cancelAlarm(String notificationId, Class<? extends AbstractReceiver> receiverClass)
        throws BroadcastException {
    /*
     * Create an intent that looks similar, to the one that was registered using add. Making sure the notification id in the action is the same. Now we can search for
     * such an intent using the 'getService' method and cancel it.
     */
    final Intent intent = new Intent(this.context, receiverClass);
    intent.setAction(notificationId);

    final PendingIntent pi = PendingIntent.getBroadcast(this.context, 0, intent, 0);
    final AlarmManager am = getAlarmManager();

    try {
        am.cancel(pi);
    } catch (Exception e) {
        Log.e(this.getClass().getSimpleName(), e.getMessage());
        throw new BroadcastException(e.getMessage());
    }
}

private void addOrUpdateAlarm(...){
    try {
        cancelAlarm(notificationId, OurReceiver.class);
    } catch (BroadcastException e) {
        Log.e(AlarmHelper.class.getSimpleName(), "addOrUpdateAlarm: Can't cancel current alarm before reinserting.", e);
    }

    Intent intent = new Intent(this.context, receiverClass);
    intent.setAction(notificationId);
    // some intent.setExtra() calls.
    PendingIntent sender = PendingIntent.getBroadcast(this.context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    /* Get the AlarmManager service */
    final AlarmManager am = getAlarmManager();

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
    }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
        am.setExact(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
    }else{
        am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
    }

}

然后在 OurReceiver 中创建一个通知通道:

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationManager mNotificationManager =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel mChannel = new NotificationChannel(id, name, importance);
        // Configure the notification channel.
        mChannel.setDescription(description);
        mChannel.enableLights(true);
        // Sets the notification light color for notifications posted to this
        // channel, if the device supports this feature.
        mChannel.setLightColor(Color.RED);
        mChannel.enableVibration(true);
        mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
        mNotificationManager.createNotificationChannel(mChannel);
    }

最后发送通知:

    PendingIntent pIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), intent, 0);

    Notification n = new NotificationCompat.Builder(context, channel)
            .setContentTitle(notificationTitle)
            .setContentText(notificationSubText)
            .setSmallIcon(R.drawable.logo)
            .setContentIntent(pIntent)
            .setDefaults(Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE)
            .setAutoCancel(true).build();

    NotificationManager notificationManager = 
              (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

            notificationManager.notify(0, n); 

【问题讨论】:

  • 检查无法接收通知的设备的注册令牌。它存在还是不存在??
  • 嘿@FazalHussain,没有远程通知。都是本地的。
  • 您是否在 Boot_Complete 操作清单中设置了 BroadcastReceiver?
  • addOrUpdateAlarm 方法何时被调用?
  • @AgonAvdijaj 是的。我们在自己的设备上收到通知这一事实应该支持这一点。

标签: android alarmmanager android-notifications


【解决方案1】:

关于通知,我可以告诉你以下几点:

在开发一个使用大量来自 AlarmManager 的Alarms 的应用程序之后,我发现了很多关于一些冲突设备的事情。 (我没有尝试过 JobScheduler,但在大多数情况下,这项技术也会失败)。

有一些厂商(众所周知,华为、小米、三星等)干扰了AlarmManager的生命周期。

华为和小米

默认情况下,华为在锁定屏幕时会杀死所有不受保护的应用程序。就是这样,杀死了应用程序的所有资源,包括alarmsboradcast receiversservices。该应用程序在屏幕锁定后被完全杀死,因此不会收到alarms,并且通知不会在逻辑上显示。为了避免这种情况,华为提供了一种将应用程序置于保护模式的方法,这意味着当屏幕被锁定时,这些受保护的应用程序不会被杀死。因此,受保护的应用仍会收到 alarmsbroadcast receivers

三星

三星有一个“功能”(开发人员不需要的功能)与华为和小米设备“相同”,但略有不同。三星在锁定屏幕时不会杀死未受保护的应用程序,而是在应用程序在 3 天内未打开时杀死。用户闲置 3 天后,该应用程序像华为和小米一样被终止,因此未收到通知 (alarms)。三星还提供了一种保护应用程序并避免这种情况的方法。

结论

还有其他制造商有相同的行为,但我不知道所有这些制造商。我可以告诉你,华为、小米和三星是其中最知名的。

因此,首先尝试了解是否所有故障设备都是由这些相互冲突的制造商制造的。

然后,有一些方法可以让用户知道通知可能不会在此设备中触发,并邀请他们将您的应用程序作为受保护的应用程序。

以编程方式,您可以执行类似的操作 (source):

if("huawei".equalsIgnoreCase(android.os.Build.MANUFACTURER)) { 
    AlertDialog.Builder builder  = new AlertDialog.Builder(this);
    builder.setTitle(R.string.huawei_headline).setMessage(R.string.huawei_text)
            .setPositiveButton(R.string.go_to_protected, new DialogInterface.OnClickListener() {
                @Override 
                public void onClick(DialogInterface dialogInterface, int i) {
                    Intent intent = new Intent();
                    intent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
                    startActivity(intent);
                } 
            }).create().show(); 
}

您也可以对其他有冲突的制造商做同样的事情。这就是我在这些设备中处理这种情况的方式,并且在大多数情况下,通知都能正常工作。

希望这会有所帮助。

【讨论】:

  • 这很有趣,你有链接来源吗?
  • @Alessandro.Vegna 不幸的是我没有。当我调查它时,我没有发现任何关于这个的东西。所有这些已知信息都在我的脑海中,它们是对未显示的通知或意外终止的后台服务以及此类事情进行艰苦而长期调查的产物。所以我通过电源管理器找到了解决方案,使应用程序受到保护。
  • 好的,没问题,但我可以确认三星的那个,因为我的应用程序运行良好,并且在 3 天后它停止了。无论如何谢谢=)
  • dontkillmyapp.com 正在记录这种事情 :-)
【解决方案2】:

一段时间后您没有收到任何通知的问题是因为DOZE

您有 2 个选项来解决您的问题:

【讨论】:

  • developer.android.com/training/monitoring-device-state/… 似乎声明 setExactAndAllowWhileIdle 实际上忽略了打盹模式。你确定吗?
  • 请注意,alarm 会忽略打瞌睡,而不是通知,因此即使警报会调用显示通知的服务,我也不确定您是否会能够看到它。还要记住,Doze 切断了所有的网络连接,所以如果你需要使用互联网检查某些东西,它肯定是行不通的
  • 哇。这是一个偷偷摸摸的警告。感谢您的澄清!
【解决方案3】:

原因可能是Doze。 它的工作原理是,当设备稳定且不使用时,android会与其他应用程序一起进行后台调用,以节省手机的唤醒时间并节省电池电量。我不确定这是否是您的情况,但您可能会调查一下。

This answer might help you in doing so.

【讨论】:

    【解决方案4】:

    一些文档说应用最多可以处理 50 个通知。(不确定)

    但如果您尝试提供不同的通知 ID:

    for (int i...>500)
     notificationManager.notify(i, n);
    

    希望这可行

    【讨论】:

    • 你有关于这个最大值的任何来源吗?
    【解决方案5】:

    @Mars Estrada 是对的,问题出在电话实现上。 华为有一个名为 powergenie 的软件,它可以杀死(在 linux 术语中)你的服务。 所以实现显然不尊重打瞌睡模式。有些应用程序只是告诉用户“既然你有华为,请务必正确设置电源管理”。 小米和三星也是如此。由于这些实现不遵循 Android SDK,因此无法管理后台应用程序。 对于我针对不同华为的应用(HOnor 8x, Huawei P20, P10, P9, P9 lite),如果应用在设置中没有后台执行保护,甚至无法使用Context.startService(或者startFroegroundService的时候启动一个服务)可用的)。 问候 查尔斯

    【讨论】:

      【解决方案6】:

      也许我的回答会节省一些人的时间 - 在我的情况下,没有在特定设备上收到通知的原因仅仅是因为设备上的“请勿打扰”模式。

      【讨论】:

        猜你喜欢
        • 2013-10-08
        • 1970-01-01
        • 1970-01-01
        • 2022-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多