我正在开发一个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中我们创建一个notificationchannel:
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);
答案 0 :(得分:10)
关于通知,我可以告诉您以下内容:
经过很长一段时间开发一个使用来自AlarmManager的大量Alarms
的应用程序后,我发现了一些关于某些冲突设备的东西。 (我没有尝试过JobScheduler,但在大多数情况下,这项技术也会失败)。
有些制造商(众所周知,华为,小米,三星等)会干扰AlarmManager
的生命周期。
华为和小米
华为默认在锁定屏幕时会杀死所有未受保护的应用程序。这样就可以杀死该应用的所有资源,包括alarms
,boradcast receivers
和services
。屏幕锁定后,应用程序将被完全杀死,因此未收到alarms
,并且逻辑上未显示通知。为了避免这种情况,华为提供了一种将应用程序置于保护模式的方法,这意味着当屏幕被锁定时,这些受保护的应用程序不会被杀死。然后,受保护的应用仍会收到alarms
和broadcast receivers
。
<强>三星强>
三星有一个&#34;功能&#34; (开发人员不需要的功能)执行&#34;相同&#34;比华为和小米设备有一点差别。锁定scree时,三星不会杀死不受保护的应用程序,但是当应用程序未在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();
}
你可以和其他冲突的制造商一样做。这就是我在这些设备中处理这种情况的方法,并且在大多数情况下通知都很有效。
希望这有帮助。
答案 1 :(得分:5)
一段时间后没有收到任何通知的原因是DOZE。
您有2个选项可以解决您的问题:
&#34;删除&#34;从你的应用程序打瞌睡(https://www.greenbot.com/article/2993199/android/how-to-turn-off-doze-mode-for-specific-apps-in-android-marshmallow.html)(不推荐,每个用户必须这样做,你不能将其设置为默认值)
根据文档使用FCM高优先级:
*打盹清单
如果可能,请使用FCM进行下游消息传递。 如果您的用户必须立即看到通知,请务必使用FCM high priority message。 在初始消息有效负载内提供足够的信息,因此不需要后续的网络访问。 使用setAndAllowWhileIdle()和setExactAndAllowWhileIdle()设置严重警报。 在Doze中测试你的应用程序。*
答案 2 :(得分:2)
原因可能是Doze。 它可以工作,当设备稳定且不使用时,android会与其他应用程序一起进行后台调用,以节省手机的唤醒时间并节省电池电量。我不确定这是否是你的情况,但你可能会考虑到这一点。
答案 3 :(得分:1)
有些文档称app最多可以处理50个通知。(不确定)
但如果您尝试提供不同的通知ID:
for (int i...>500)
notificationManager.notify(i, n);
希望这有效
答案 4 :(得分:0)