android AlarmManager使用初探
引子:
可悲的5月29号,由于在android手机中安装的“正点闹钟”没有响,让我错过了欧冠联赛决赛。完美的巴萨用完美的进球赢了完美的比赛,可我只能在第二天,看看下载的比赛实况,聊以自慰。痛定思痛,我觉得做个简单的闹钟程序。但事情好像并不那么简单……
通过粗略查看SDK,初步决定使用service,在service中注册Intent.ACTION_TIME_TICK事件的BroadcastReceiver,每隔一分钟遍历一次定时器表,来判断是否响铃。但在测试中遇到了许多问题,试举几例:
- 普通优先级的service会在系统资源不够时,被自动回收(kill掉)。
- 被kill掉后,有时能自动被重新启动,有时却不能。
- service.setForeground在2.3系统中不再被支持。
- service.startForeground(设置前台服务模式)可以支持,但必须在状态栏一直显示notification icon。
- 前台服务模式可能会阻止设备深度休眠,进而增加设备耗电量。(条件所限,无法测试,所以不能确定,仅仅推测。)
在测试中,根据遇到的这些问题,对症下药,最终一一解决,但最后一个问题一直让我放心不下,后来通过查询stackoverflow,并收到同事的建议,决定用AlarmManager实现闹钟,因为它至少可以保证不会出现最后一个问题。这是查到的两个讨论:
- http://stackoverflow.com/questions/5120185/android-sleep-standby-mode
- http://stackoverflow.com/questions/5007721/android-sleep-stages-levels-on-an-android-device
正文:
这里也有两篇很好的文章来讲如何使用AlarmManager,请先参考:
- https://github.com/commonsguy/cw-advandroid/tree/master/SystemServices/Alarm 这是源代码,结构很清晰,代码全是自注释的。
- http://aswang.iteye.com/blog/875863 这是中国程序员写的,言简意赅,特别是倒数第二段话,曾帮了我大忙。
在这两篇文章之外,我仅列举一些编程时的注意点:
- 设置闹钟使用AlarmManager.set()函数,它的triggerAtTime参数,如果要用Calendar.getTimesInMillis()获得,就必须先设置Calendar对象,例如要让闹钟在当天的16:30分启动,就要设置HOUR_OF_DAY(16)、MINUTE(30)、MILLISECOND(0),特别是HOUR_OF_DAY,我一开始误用了HOUR,这是12进制计时方法,HOUR_OF_DAY是24进制计时方法。
- 针对同一个PendingIntent,AlarmManager.set()函数不能设置多个alarm。调用该函数时,假如已经有old alarm使用相同的PendingIntent,会先取消(cancel)old alarm,然后再设置新的alarm。如何判断是否已经有相同的PendingIntent,请看下条。
- 取消alarm使用AlarmManager.cancel()函数,传入参数是个PendingIntent实例。该函数会将所有跟这个PendingIntent相同的Alarm全部取消,怎么判断两者是否相同,android使用的是intent.filterEquals(),具体就是判断两个PendingIntent的action、data、type、class和category是否完全相同。
- 在AndroidManifest.xml中静态注册BroadcastReceiver时,一定使用android:process=":xxx"属性,因为SDK已注明:If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the broadcast receiver runs in that process.