
文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载导读本文基于 android-training-course-in-chinese 仓库中 background-jobs/scheduling/alarms.md 一课系统讲解 Android 平台下基于AlarmManager的重复闹钟调度机制从闹钟类型选型、setInexactRepeating()与setRepeating()的精度取舍、到设备重启后恢复闹钟的完整流程。读完本文你将掌握如何在应用未运行、设备睡眠的情况下可靠地执行周期任务如每日后台同步天气数据并遵循官方最佳实践将电量损耗与服务器负荷降到最低。为什么需要闹钟应用生命周期之外的定时任务闹钟Alarm基于 Android 系统服务AlarmManager实现它赋予开发者一种在应用使用期之外执行与时间相关操作的能力。典型的应用场景是每天固定时间启动一次后台服务下载当日的天气预报——此时应用本身可能已经不在前台、甚至进程已被系统回收。闹钟的四个核心特性如下时间驱动允许通过预设时间或设定时间间隔来触发Intent与广播结合可以将闹钟与BroadcastReceiver组合启动 Service 并执行其他操作脱离应用生命周期闹钟可在应用范围之外执行即使应用没有运行、设备处于睡眠状态也能触发事件或行为节省资源帮助应用最小化资源占用用闹钟调度任务来替代 Timer 或长时间连续运行的后台服务。Note对于只需要在应用使用期之内发生的定时操作官方同样推荐使用闹钟来替代HandlerTimerThread的组合方案因为前者能让 Android 系统更好地统筹系统资源、统一调度。AlarmManager并非唯一的后台调度手段它属于 background-jobs 体系的一部分。同一小节内还有 保持设备唤醒 一课与之互补闹钟解决何时被唤醒的调度问题Wake Lock 解决唤醒后 CPU 持续运行的问题。如果被闹钟唤醒后需要执行较长的后台任务通常需要二者配合或使用WakefulBroadcastReceiver管理唤醒锁生命周期。权衡利弊何时不该用重复闹钟重复闹钟的机制比较简单灵活性有限。特别是当你要用它来触发网络操作时需要格外谨慎——设计不佳的闹钟会导致电量快速耗尽并对服务端产生巨大负荷。当我们需要从服务端同步数据、且应用在后台被唤醒执行同步操作时看似应该使用重复闹钟但如果存储数据的服务端由你控制Google Cloud MessagingGCM Sync Adapter 是更好的解决方案。SyncAdapter 提供的任务调度选项与AlarmManager基本相同但灵活性更高同步触发可基于服务端或设备发出的新数据提示消息、用户操作或不操作、每天的某一时刻等条件。关于 SyncAdapter 的调度时机服务端数据变更、设备数据变更、系统网络消息、定时、按需可以参考仓库中的 执行 Sync Adapter 一课它与本课的选型建议互为印证。最佳实践方法设计重复闹钟时的每一个决定都可能影响应用对系统资源的消耗。以每个应用实例在下午十一点整从服务器同步数据为例所有实例在同一时刻请求服务器会造成服务器响应变慢甚至拒绝服务。因此使用闹钟时应牢记以下官方最佳实践对重复闹钟触发的网络请求添加随机性抖动jitter闹钟触发时先做本地任务不需要访问服务器的任务对包含网络请求的闹钟在调度时机上增加随机性避免所有实例在同一瞬间打满服务器尽量降低闹钟频率非必要不唤醒设备这取决于闹钟类型见下文选择闹钟类型触发时间不必过度精确尽量使用setInexactRepeating()替代setRepeating()。使用前者时Android 系统会集中多个应用的重复闹钟同步请求并一起触发减少设备被唤醒的总次数从而降低电量消耗。注意从Android 4.4API Level 19开始所有重复闹钟都已是非精确类型尽量避免让闹钟基于时钟时间精确时刻触发重复闹钟比较困难应尽可能使用ELAPSED_REALTIME系列类型下文详细展开。仓库中的 最小化定期更新造成的影响 一课提供了与本课互补的实战细节轮询更新时应优先使用ELAPSED_REALTIME或RTC而非_WAKEUP变体让系统在闹钟触发前尽量保持待机并根据应用最近使用频率对更新周期执行指数退避算法exponential back-off在上次更新后应用未被使用的情况下成倍拉长更新间隔SharedPreferences sp context.getSharedPreferences(PREFS, Context.MODE_WORLD_READABLE); boolean appUsed sp.getBoolean(PREFS_APPUSED, false); long updateInterval sp.getLong(PREFS_INTERVAL, DEFAULT_REFRESH_INTERVAL); if (!appUsed) if ((updateInterval * 2) MAX_REFRESH_INTERVAL) updateInterval MAX_REFRESH_INTERVAL; Editor spEdit sp.edit(); spEdit.putBoolean(PREFS_APPUSED, false); spEdit.putLong(PREFS_INTERVAL, updateInterval); spEdit.apply(); rescheduleUpdates(updateInterval); executeUpdateOrPrefetch();设置重复闹钟四个关键要素对于定期执行的任务或数据查询重复闹钟是不错的选择。创建一个重复闹钟需要确定以下四个要素要素说明闹钟类型决定时间基准与是否唤醒设备下一节展开触发时间若触发时间是过去的时间点闹钟会被立即触发闹钟间隔时间例如一天一次、每小时一次、每五秒一次Pending Intent闹钟被触发时才发出的 Pending Intent若为同一个 Pending Intent 再设置闹钟会覆盖第一个选择闹钟类型两大时间基准 × 唤醒能力使用重复闹钟首先要确定闹钟类型。闹钟类型分为两大类ELAPSED_REALTIME从系统启动之后开始计算流逝时间。不受地区和时区影响适合基于时间差的闹钟例如每 30 秒触发一次REAL_TIME_CLOCKRTC使用世界统一时间UTC适合依赖具体地区时刻的闹钟例如每天下午 2 点。两类闹钟都还有唤醒WAKEUP版本可以在设备屏幕关闭时唤醒 CPU确保闹钟在既定时间被激活——这对实时性要求较高的应用如对执行时间有要求的操作非常有效。如果未使用唤醒版本所有重复闹钟将在设备下一次被唤醒时统一激活。具体的四种闹钟类型如下表类型行为ELAPSED_REALTIME设备启动后度过特定时间后激活 Pending Intent不唤醒设备设备睡眠时间包含在内ELAPSED_REALTIME_WAKEUP设备启动后度过特定时间后唤醒设备并激活 Pending IntentRTC在某一特定时刻激活 Pending Intent不唤醒设备RTC_WAKEUP在某一特定时刻唤醒设备并激活 Pending Intent选型建议只想让闹钟按特定时间间隔激活如每半小时一次用任意一种ELAPSED_REALTIME类型通常是更好的选择闹钟需在每天特定时刻激活才选RTC类型。但要警惕其缺陷地区时区变化时应用可能无法正确应对用户修改设备时间会引发预期之外的行为同时RTC还存在精度问题——因此官方强烈建议尽可能使用ELAPSED_REALTIME类型。案例一ELAPSED_REALTIME_WAKEUP 示例每隔 30 分钟唤醒设备以激活闹钟非精确重复// Hopefully your alarm will have a lower frequency than this! alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);一分钟后唤醒设备并激活一个一次性无重复闹钟private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent new Intent(context, AlarmReceiver.class); alarmIntent PendingIntent.getBroadcast(context, 0, intent, 0); alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() 60 * 1000, alarmIntent);注意一次性闹钟使用set()方法触发时刻基于SystemClock.elapsedRealtime()当前值加偏移毫秒数60 * 1000即 60 秒。案例二RTC 示例大约下午 2 点唤醒设备并激活闹钟以天为周期重复非精确// Set the alarm to start at approximately 2:00 p.m. Calendar calendar Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 14); // With setInexactRepeating(), you have to use one of the AlarmManager interval // constants--in this case, AlarmManager.INTERVAL_DAY. alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntent);精确地在上午 8:30 唤醒设备此后每 20 分钟重复一次private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent new Intent(context, AlarmReceiver.class); alarmIntent PendingIntent.getBroadcast(context, 0, intent, 0); // Set the alarm to start at 8:30 a.m. Calendar calendar Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 8); calendar.set(Calendar.MINUTE, 30); // setRepeating() lets you specify a precise custom interval--in this case, // 20 minutes. alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 20, alarmIntent);注意两个细节使用setInexactRepeating()时只能使用AlarmManager提供的间隔常量如INTERVAL_DAY而setRepeating()允许自定义精确的间隔毫秒数如1000 * 60 * 20表示 20 分钟。决定闹钟的精确度setInexactRepeating 与 setRepeating创建闹钟第二步是决定精度setInexactRepeating()是大多数应用的正确选择。Android 系统会集中多个应用的重复闹钟同步请求并一起触发从而减少电量损耗setRepeating()仅适用于实时性要求较高的场景例如闹钟需要精确地在上午 8:30 激活、此后每小时重复一次但应尽量避免使用精确闹钟两者间隔参数规则不同setRepeating()支持自定义间隔setInexactRepeating()只能使用系统间隔常量例如间隔常量时长INTERVAL_FIFTEEN_MINUTES15 分钟INTERVAL_HALF_HOUR30 分钟INTERVAL_HOUR1 小时INTERVAL_HALF_DAY12 小时INTERVAL_DAY24 小时完整的常量列表与默认值可查阅AlarmManager类参考文档。仓库内的实战佐证TV 应用推荐服务 一课中推荐服务需要周期性运行以保证推荐内容更新官方示例就是通过BroadcastReceiver结合setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, INITIAL_DELAY, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent)来调度每半小时的周期性执行其中INITIAL_DELAY为 5 秒——与本课的选型建议完全一致非精确、固定间隔常量、设备启动后调度。取消闹钟需要为用户提供取消闹钟的能力时调用AlarmManager的cancel()方法并把不想激活的PendingIntent传入即可// If the alarm has been set, cancel it. if (alarmMgr! null) { alarmMgr.cancel(alarmIntent); }注意cancel()依赖PendingIntent的匹配规则传入的 PendingIntent 必须与设置闹钟时使用的是同一个即相同的 Intent 与 requestCode系统才能找到并移除对应的闹钟。设备重启后启用闹钟BOOT_COMPLETED 完整流程默认情况下所有闹钟会在设备关闭时被取消。如果希望闹钟在用户重启设备后自动恢复从而让AlarmManager继续工作、无需用户手动重设需要完成以下三步。第 1 步声明权限在应用 Manifest 中声明RECEIVE_BOOT_COMPLETED权限允许应用接收系统启动完成后的ACTION_BOOT_COMPLETED广播只有在用户至少启动过一次你的应用后才有效uses-permission android:nameandroid.permission.RECEIVE_BOOT_COMPLETED/第 2 步实现接收器实现BroadcastReceiver接收启动完成广播并在其中重新设置闹钟public class SampleBootReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(android.intent.action.BOOT_COMPLETED)) { // Set the alarm here. } } }第 3 步注册接收器并设置 enabled 属性在 Manifest 中添加接收器Intent-Filter 接收ACTION_BOOT_COMPLETED这个 Actionreceiver android:name.SampleBootReceiver android:enabledfalse intent-filter action android:nameandroid.intent.action.BOOT_COMPLETED/action /intent-filter /receiver这里的关键在于android:enabledfalse除非应用显式启用它否则该接收器不会被调用防止其被不必要地触发。当用户设置闹钟时通过PackageManager动态启用该接收器ComponentName receiver new ComponentName(context, SampleBootReceiver.class); PackageManager pm context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);一旦启用接收器将一直保持启动状态即使设备重启也不例外——代码中设置的启用配置会覆盖 Manifest 中的现有配置。它保持启用直到应用显式禁用例如用户取消闹钟时ComponentName receiver new ComponentName(context, SampleBootReceiver.class); PackageManager pm context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);这种默认禁用 按需启用的动态组件开关模式是 Android 组件管理的标准实践也被 TV 推荐服务示例采用——见 tv/discovery/recommendations.md 中BootupActivity在ACTION_BOOT_COMPLETED时调度每半小时推荐更新的实现。小结闹钟调度的黄金准则时间基准优先选ELAPSED_REALTIME尽量避开RTC时区、时间修改、精度三大风险精度优先选setInexactRepeating()只有实时性硬需求才用setRepeating()网络请求必须加抖动、降频率、用非唤醒类型结合指数退避算法降低服务器与电量压力能由服务端推送GCM SyncAdapter解决的同步需求不要用轮询式闹钟配合 保持设备唤醒 一课的知识在闹钟触发的后台任务执行期间正确使用 Wake Lock 或WakefulBroadcastReceiver保证任务完整执行。更完整的后台任务调度体系包含 CursorLoader 后台加载、后台服务、闹钟与唤醒锁可继续阅读 background-jobs 目录下的系列课程。赞分享文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载相关推荐Webnovel Writer /webnovel-plan 完整指南AI 写长篇网文如何拆章与建卷时间线Webnovel Writer /webnovel plan 完整指南AI 写长篇网文如何拆章与建卷时间线 长篇网文辅助创作系统 Webnovel Write文档教程移动开发Ant Design Form 中时间类组件的 dayjs 值处理提交前的格式化与校验实践Ant Design Form 中时间类组件的 dayjs 值处理提交前的格式化与校验实践 导读 在 Ant Designantd的 Form 体系中文档教程移动开发Android 官方培训课程中文版精读使用 Wi-Fi P2P 服务发现实战指南Android 官方培训课程中文版精读使用 Wi Fi P2P 服务发现实战指南 本文基于 android training course in chines文档教程移动开发上一篇终极指南3步配置SPT-AKI Profile Editor轻松管理你的逃离塔科夫离线存档下一篇CompressO免费开源的终极媒体压缩方案一键将视频图片缩小90%创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考