最近,我一直在收到用户提供的有关我的闹钟应用程序的信息。最后,其中一个用户从build中发送了我的信息,这真的很奇怪:
74. 4:25:0 - StartAlarm received
75. 5:22:15 - AlarmOnScreen create
76. 5:22:15 - Time: 04:25
问题是,记录的信息保存如下:
//BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent) {
Logger.initialize(context);
Logger.log("StartAlarm received");
Intent i = new Intent(context, AlarmOnScreen.class);
i.putExtras(intent.getExtras());
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
//AlarmOnScreen (activity)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.alarm_on_screen);
Logger.log("AlarmOnScreen create");
//Time value of alarm is logged below
(...)
如您所见,活动开始时间大大延迟。怎么可能?用户报告说,警报被延迟,直到他开始“使用”手机 - 我猜这意味着,直到锁屏被解锁或屏幕开启。我还在等待更多信息的回答。在其他时间延迟只有5分钟 - 每次,直到用户开始“使用电话”
有什么想法吗?
编辑: 让我补充一点,这是在应用程序出现数月后最近才开始发生的事情。我仍然在寻找是否可能在清单和最新更新中更改了任何内容,但它是否可能仅在新的Android版本上发生?
答案 0 :(得分:5)
我认为您的问题是在没有正确使用WakeLocks的情况下使用AlarmManager,当设备在屏幕关闭时“休眠”,您的接收器将无法正常工作。
我猜你的接收器来自AlarmManager的onReceive(),很可能是以这样的_WAKEUP
标志开始的:
mAlarmManager.set(AlarmManager.RTC_WAKEUP, .......);
此_WAKEUP标志表示即使设备处于睡眠模式,设备也会“开启”。 但是,正如此处的文档所述(http://developer.android.com/reference/android/app/AlarmManager.html):
只要警报接收器的onReceive()方法正在执行,警报管理器就会保持CPU唤醒锁定。这可以保证在您完成广播处理之后手机不会睡眠。一旦onReceive()返回,Alarm Manager就会释放此唤醒锁定。这意味着在某些情况下,只要onReceive()方法完成,手机就会休眠。如果您的警报接收器调用了Context.startService(),则手机可能会在启动所请求的服务之前休眠。为了防止这种情况,您的BroadcastReceiver和服务需要实施单独的唤醒锁定策略,以确保手机继续运行,直到服务可用。
在您的代码中,这意味着系统会在onReceive()
结束后立即重新进入睡眠状态,并且由于startActivity(i)
无法同步工作 - 这会直接导致问题,如上所述 - 它将是推出,但很久以后,只是当用户打开屏幕时。
要解决这个问题,我建议你这样做:
//BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent) {
Logger.initialize(context);
Logger.log("StartAlarm received");
Intent i = new Intent(context, AlarmOnScreen.class);
i.putExtras(intent.getExtras());
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
AlarmOnScreen.acquireLock(context);
//Before, system could sleep right after this line(not exactly, however) and activity actually would be started much later
}
//AlarmOnScreen (activity)
private static WakeLock sWakeLock;
public static void acquireLock(Context context) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
sWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "providersLock");
//Limit 10 sec, if something wrong will happen - we'll not drain the battery to much.
sWakeLock.acquire(10000);
//As we are acquiring and releasing only once - we don't need a counter.
sWakeLock.setReferenceCounted(false);
}
private static void releaseLock(Context context) {
try {
sWakeLock.release();
} catch (Exception e) {
//In case it's already auto-released
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.alarm_on_screen);
Logger.log("AlarmOnScreen create");
//Time value of alarm is logged below
(...)
@Override
protected void onResume() {
releaseLock(this);
}
此解决方案将首次运行,让您更深入地了解问题。测试 - 只是在屏幕关闭时开始使用你的闹钟,并且可能是电缆被拔掉,但我不确定是否真的需要让设备进入睡眠模式。
但是,我强烈建议实施更适合您项目的更优雅的解决方案,因为当前的静态参考设计相当差,因为它在赛车条件下无法完美运行。
希望它有所帮助,如果有任何问题,请告诉我。 祝你好运。
<强> UPD:强> 我想我也建议不仅使用PARTIAL_WAKE_LOCK,而且还要使用全部。像:
pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
| PowerManager.ACQUIRE_CAUSES_WAKEUP, "providersLock");
这将迫使屏幕开启,而不依赖于先前的状态和平台对新活动创建的反应。
答案 1 :(得分:0)
您必须在活动窗口中设置几个标志:
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
);