活动开始延迟(startActivity)错误

时间:2012-09-07 08:04:56

标签: android

最近,我一直在收到用户提供的有关我的闹钟应用程序的信息。最后,其中一个用户从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版本上发生?

2 个答案:

答案 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
);
相关问题