报警管理器可靠性
我已经为这个问题苦苦挣扎了好几天。我还检查了文档和几个主题,但没有找到任何解决方案/解释。
我正在 LG p500 上测试我的应用程序,但我也在 Droid 上做了一些测试,得到了相同的结果。
我的应用程序使用 AlarmHandler 来安排警报。应用程序在模拟器和设备上都能正常运行,直到设备有足够的可用内存。 当我在设备上启动其他几个应用程序并且内存不足时,警报将不再触发。一旦我停止“其他”应用程序,警报就会再次正常工作。
让我报告测试和结果。
- 10 分钟后我在我的应用程序上设置了闹钟。
- 我启动了几个应用程序(浏览器、谷歌地图、gmail、K9Mail,....)
- 我启动目录以查看我的应用程序的日志
- 等待 15 分钟,而不在手机上工作
- 10 分钟后,警报应该被触发,但什么也没有发生,直到我按下按钮唤醒手机
- 当我唤醒手机时,警报会立即响起,并且所有通知都会发生。
- 我停止之前启动的“其他”应用程序(浏览器、谷歌地图等)
- 10 分钟后再次设置闹钟
- 我启动目录以查看应用程序的日志
- 等待而不使用手机
- 10 分钟后闹钟触发我会收到通知。
我多次进行了这个测试,得到了相同的结果。
然后我尝试使用我之前从市场下载的“Catch”应用程序设置警报,并且得到相同的行为,因此看起来这不是我的应用程序的问题。
查看我的应用程序的日志,我没有看到任何错误/异常,但看起来当系统内存不足时会发生某些事情,并且广播接收器不会启动,直到通过键盘唤醒手机。一旦我唤醒手机,接收器就会启动并且所有通知都会发生。
这里是我使用的代码:
接收器:
public class NotificationReceiver extends BroadcastReceiver
{
public static final String LOG_TAG = "YAAS - Notification Receiver";
@Override
public void onReceive(Context context, Intent intent)
{
ScheduleActivityService.acquireStaticLock(context);
Log.i(LOG_TAG, "Received alarm - id: " + intent.getIntExtra("id", -1));
Intent intent2 = new Intent(context, ScheduleActivityService.class);
intent2.putExtra("id", intent.getIntExtra("id", -1));
context.startService(intent2);
}
}
服务
public class ScheduleActivityService extends Service
{
public static final String LOCK_NAME_STATIC="it.hp.yaas.AppService.Static";
public static final String LOG_TAG = "YAAS - ActivityService";
private static PowerManager.WakeLock lockStatic = null;
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder
{
public ScheduleActivityService getService()
{
return ScheduleActivityService.this;
}
}
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
public static void acquireStaticLock(Context context) {
getLock(context).acquire();
}
synchronized private static PowerManager.WakeLock getLock(Context context)
{
if (lockStatic == null)
{
PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOCK_NAME_STATIC);
lockStatic.setReferenceCounted(true);
}
return(lockStatic);
}
/**
* This method is called when an alarm fires that is its alarm time is reached.
* The system assume that the alarm fired match the alarm time of the first
* activity.
* @param intent intent fired
* @param flag
* @param startId
*/
@Override
public int onStartCommand(Intent intent, int flag, int startId)
{
super.onStartCommand(intent, flag, startId);
try {
Log.i(LOG_TAG, "Alarm fired: " + startId + " - id: " + intent.getIntExtra("id", -1));
AlarmHandler.getInstance().onAlarmFired(intent.getIntExtra("id", -1));
}
finally { getLock(this).release(); }
return START_STICKY;
}
@Override
public void onDestroy()
{
super.onDestroy();
Log.i(LOG_TAG, "Destroy");
}
}
来自 AlarmHandler 的一段代码,调用来安排警报的例程:
public synchronized void onAlarmFired(int alarmId)
{
scheduledAlarmId = -1;
Alarm alarmFired = pop();
if (alarmFired == null) return;
Log.i(LOG_TAG, "onAlarmFired (Alarm: " + alarmFired + ") at (time: " + Utilities.convertDate(new Date(), "HH:mm:ss") + ")");
notifyAlarmListener(alarmFired);
if (alarmFired.reschedule(null) != null) add(alarmFired);
Alarm alarm = peek();
if (alarm != null && scheduledAlarmId != alarm.getId()) scheduleEvent(alarm);
}
/**
* Schedule an alarm through AlarmManager that trigger next activity notification
* @param alarm alarm to be scheduled
*/
private void scheduleEvent(Alarm alarm)
{
Log.i(LOG_TAG, "scheduleEvent - (Alarm: " + alarm + ")");
Intent intent = new Intent(context, NotificationReceiver.class);
intent.putExtra("id", alarm.getId());
// In reality, you would want to have a static variable for the request code instead of 192837
PendingIntent sender = PendingIntent.getBroadcast(context, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, alarm.getTime().getTime(), sender);
scheduledAlarmId = alarm.getId();
}
最后这是一段清单文件:
<activity android:name=".ListActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".EditActivity"/>
<activity android:name=".SettingsActivity"/>
<service android:name="ScheduleActivityService"
android:label="YAAS Service"/>
<receiver android:name="NotificationReceiver" />
I've been struggling with this problem for days. I've also checked the documentation and several topics but didn't find any solution / explanation.
I am testing my application on LG p500 but I did a few test on Droid too and I get the same result.
My application uses AlarmHandler to schedule alarm. The application works correctly on the emulator and also on the device until the device has enough free memory.
When I start several other applications on the device and the memory is low the alarm will not fire anymore. As soon as I stop the "other" application the alarm works fine again.
Let me report the test and the result.
- I set an alarm on my application 10 minute later.
- I start several application (browser, google map, gmail, K9Mail,....)
- I start the catlog to see the log of my application
- Wait 15 minute without working on the phone
- After 10 minutes the alarm should be fired but nothing happen until I wakeup my phone pressing a button
- When I wake-up my phone the alarm immediatly fires and all the notificatin happen.
- I stop the "other" application I previously started (browser, google map,...)
- Set again an alarm 10 minute later
- I start the catlog to see the log of my application
- Wait without working on the phone
- 10 minutes later the alarm fires and I get notified.
I did this test several time and I get the same result.
Then I tried to set an alarm using the "Catch" application I previously downloaded from the market and I get the same behaviour so it looks like this is not a problem of my application.
Looking at the log of my application I do not see any error / exception but it looks like that when the system is low on memory something happen and the broadcast receiver does not start until the phone is waked up throught the keyboard. As soon as I wake-up the phone the receiver start and all the notification happen.
Here the code I used:
The Receiver:
public class NotificationReceiver extends BroadcastReceiver
{
public static final String LOG_TAG = "YAAS - Notification Receiver";
@Override
public void onReceive(Context context, Intent intent)
{
ScheduleActivityService.acquireStaticLock(context);
Log.i(LOG_TAG, "Received alarm - id: " + intent.getIntExtra("id", -1));
Intent intent2 = new Intent(context, ScheduleActivityService.class);
intent2.putExtra("id", intent.getIntExtra("id", -1));
context.startService(intent2);
}
}
The Service
public class ScheduleActivityService extends Service
{
public static final String LOCK_NAME_STATIC="it.hp.yaas.AppService.Static";
public static final String LOG_TAG = "YAAS - ActivityService";
private static PowerManager.WakeLock lockStatic = null;
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder
{
public ScheduleActivityService getService()
{
return ScheduleActivityService.this;
}
}
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
public static void acquireStaticLock(Context context) {
getLock(context).acquire();
}
synchronized private static PowerManager.WakeLock getLock(Context context)
{
if (lockStatic == null)
{
PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOCK_NAME_STATIC);
lockStatic.setReferenceCounted(true);
}
return(lockStatic);
}
/**
* This method is called when an alarm fires that is its alarm time is reached.
* The system assume that the alarm fired match the alarm time of the first
* activity.
* @param intent intent fired
* @param flag
* @param startId
*/
@Override
public int onStartCommand(Intent intent, int flag, int startId)
{
super.onStartCommand(intent, flag, startId);
try {
Log.i(LOG_TAG, "Alarm fired: " + startId + " - id: " + intent.getIntExtra("id", -1));
AlarmHandler.getInstance().onAlarmFired(intent.getIntExtra("id", -1));
}
finally { getLock(this).release(); }
return START_STICKY;
}
@Override
public void onDestroy()
{
super.onDestroy();
Log.i(LOG_TAG, "Destroy");
}
}
An piece of code from AlarmHandler, the routine called to schedule the alarm:
public synchronized void onAlarmFired(int alarmId)
{
scheduledAlarmId = -1;
Alarm alarmFired = pop();
if (alarmFired == null) return;
Log.i(LOG_TAG, "onAlarmFired (Alarm: " + alarmFired + ") at (time: " + Utilities.convertDate(new Date(), "HH:mm:ss") + ")");
notifyAlarmListener(alarmFired);
if (alarmFired.reschedule(null) != null) add(alarmFired);
Alarm alarm = peek();
if (alarm != null && scheduledAlarmId != alarm.getId()) scheduleEvent(alarm);
}
/**
* Schedule an alarm through AlarmManager that trigger next activity notification
* @param alarm alarm to be scheduled
*/
private void scheduleEvent(Alarm alarm)
{
Log.i(LOG_TAG, "scheduleEvent - (Alarm: " + alarm + ")");
Intent intent = new Intent(context, NotificationReceiver.class);
intent.putExtra("id", alarm.getId());
// In reality, you would want to have a static variable for the request code instead of 192837
PendingIntent sender = PendingIntent.getBroadcast(context, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, alarm.getTime().getTime(), sender);
scheduledAlarmId = alarm.getId();
}
And finally this is a piece of Manifest file:
<activity android:name=".ListActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".EditActivity"/>
<activity android:name=".SettingsActivity"/>
<service android:name="ScheduleActivityService"
android:label="YAAS Service"/>
<receiver android:name="NotificationReceiver" />
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当您启动所有这些应用程序时,您确定您的进程不会被终止吗?如果是这样,你设置的闹钟就会随之消失。目前尚不清楚谁以及何时在代码中安排警报,但如果是服务,由于它是粘性的,它最终会重新启动,并且您会在某个时刻(当您唤醒设备时)收到警报。
检查在测试的不同点注册了哪些警报的简单方法:
Are you sure your process doesn't get killed when you start all those applications? If it does, the alarms you set will die with it. It's not exactly clear who and when schedules the alarm in your code, but if it's the service, since it's sticky, it will eventually gets re-started, and you will get an alarm at some point (when you wake the device).
An easy way to check what alarms are registered at different points of your testing:
我的代码与我经常编写和使用的闹钟应用程序上的代码非常相似。我无法重现您所描述的问题。我似乎无法让手机进入内存极低的状态。我打开了已安装的所有应用程序,但我的 HTC Rezound 上仍有 260M 可用空间。
作为我的应用程序中的保护措施,我使用alarmmanager.setRepeating() 而不是.set()。我将重复间隔设置为 20 秒。我像你一样将警报 ID 作为意图额外传递。当我的服务启动时,它会立即使用警报 ID 取消待处理的意图。我的逻辑是,如果由于任何原因我的闹钟失败了,它将继续每 20 秒尝试一次,直到成功。
My code is very similar to yours on an alarm app that I wrote and use regularly. I haven't been able to reproduce the problem that you describe. I can't seem to get my phone to a state of extremely low memory. I opened every app I have installed and still have 260M free on my HTC Rezound.
As a safeguard in my app I used alarmmanager.setRepeating() instead of .set(). I set the repeat interval to 20 seconds. I passed the alarm ID as an intent extra just as you have. When my service starts it immediately cancels the pending intent using the alarm ID. My logic here is that if for any reason my alarm fails it will continue to try every 20 seconds until it succeeds.
您的代码中是
AlarmManager.set()
,不能保证在您指定的时间触发。它可能会在 30 分钟甚至 6 小时后触发,我在小米 POCO F1 等设备上见过这种情况。而是使用
AlarmManager.setExact()
安排代码在特定时间运行。Android 12 引入了精确的闹钟权限。如果您不想处理这个问题,您可以使用
AlarmManager.setWindow()
来设置一个小窗口,例如 15 分钟。In your code is
AlarmManager.set()
, which is not guaranteed to fire at the time you specify. It may fire 30 minutes or even 6 hours later, which I've seen happen on devices like the Xiaomi POCO F1.Instead use
AlarmManager.setExact()
to schedule your code to run at a specific time.Android 12 introduces an exact alarms permisison. If you don't want to deal with that, you can instead use
AlarmManager.setWindow()
with a small window like 15 minutes.