*已更新:问题可能已得到解决。请直接参考下面的更新,因为AlarmManager只是部分责任。 *
我目前正在开发具有闹钟功能的Android应用。不幸的是,事实证明,似乎有一些相当具体的案例,其中AlarmManager似乎没有按预期工作。
当我最初测试应用程序时,我是通过Android Studio模拟器(Nexus 5,API 21)以及我的旧手机(Galaxy S2,API 16)进行测试的,结果是所有警报都是及时交付。我一切换了 然而,对于我的Xperia Z1 Compact(API 19),警报偶尔会突然触发几分钟。
有趣的是,这种情况似乎就是这种情况,特别是当手机正在使用电池运行时(即没有连接到电脑或插座)。有点像AlarmManager在绝望的企图放电电池时突然表现得非常迟钝,完全没有注意到它是通过 .setExact()使用的事实。如果设备未入睡,则始终及时交付。无论哪种方式,我的代码产生的行为似乎都不具有确定性,这真是令人难以置信的。
我的代码的简化版本:
首先,我根据API安排AlarmManager。根据日志日历设置为正确的日期和时间。
public abstract class AlarmScheduler {
//...
private static void schedule(AlarmManager am, PendingIntent pi, Calendar calendar){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
}
}
//...
}
计划的待处理意图是WakefulBroadcastReceiver。根据日志,每次报警都是迟到的电话 onReceive()也是如此,至少就我所见。所以问题似乎就在这里。
public class AlarmReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, AlarmService.class);
service.putExtras(intent);
startWakefulService(context, service);
}
}
为了完整性,这是BroadcastReceiver启动的服务。服务启动的活动在 onResume()中获取自己的唤醒锁。我也试图强行推迟接收器唤醒锁的释放长达1000毫秒,以保证一直处于活动状态,但这并没有产生不同的结果。
public class AlarmService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent alarmDisplay = new Intent(getBaseContext(), AlarmActivity.class);
alarmDisplay.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmDisplay.putExtras(intent);
getApplication().startActivity(alarmDisplay);
/** Some simple database operations here */
AlarmReceiver.completeWakefulIntent(intent);
return super.onStartCommand(intent, flags, startId);
}
}
老实说我在这里出错了。但是,由于闹钟可能会在某些设备上延迟发出几分钟当然是不可接受的,我会这样做 非常感谢任何意见 - 提前感谢。
更新
好吧,首先,AlarmManager.setExact()确实似乎偶尔会延迟,但只有几秒钟,而不是几分钟。根据文档,这似乎是有意的,因为它声明:
"警报将尽可能地传递到请求的触发时间。"
不幸的是,这听起来像毫秒而不是秒,这就是为什么我假设AlarmManager首先表现得很好。如果它显然迟到了几秒钟,我认为它能够延迟提供几分钟。不过,最终我的错误。
其次,最初描述的问题是一个唤醒问题,至少在我的情况下,只在我的API 19+手机上显示当前正在使用电池运行时。虽然API 19显然为了电池寿命而引入了非常积极的电源管理,但如果手机使用电池运行,管理层似乎更加激进。这似乎也是为什么在模拟器上一切都表现正常的原因(默认情况下,它总是在收费的过程中,如果要相信图标)。
最初发布的代码的问题是在活动在 onResume()中获取自己的唤醒锁之前释放了接收器唤醒锁。只需几毫秒,但仍然足以让CPU显然被推回睡眠状态。正如我在原帖中所述,我已经将此视为问题的潜在来源,这就是为什么我确实试图强行延迟唤醒锁的发布,以保证至少有一个会活跃倍。出于测试目的,我快速完成了这项工作。通过 wait()来弄脏,操作系统看起来并不是非常喜欢,因此在某些时候强行关闭我的服务,这在某些方面并没有显示出来。常规logcat输出。从这一点开始,随后的警报往往是错误的,虽然我对Android的知识不足以解释原因。
无论哪种方式,问题都可以通过获取和发布只有一个唤醒锁而不是两个独立的唤醒锁来解决(请注意,由于问题的本质,没有绝对的确定性,但测试结果看起来很好)。