16、通知的延时与定时:AlarmManager+Notification、WorkManager+Notification、JobScheduler+Notification
通知不是非得立刻弹出来的。很多时候,我们需要它「等一会儿再出现」。
比如早上8点的吃药提醒,或者半小时后的会议通知。这些场景下,通知的延时与定时就成了刚需。
Android 提供了三种主流方案:AlarmManager、WorkManager 和 JobScheduler。它们都能搭配 Notification 实现定时通知,但各有各的脾气。今天我就把这三兄弟掰开揉碎讲清楚。
核心结论先放这:
- 精确到秒级的定时任务 → 用 AlarmManager
- 后台可延迟、可保证执行的任务 → 用 WorkManager
- 系统级省电、网络相关的任务 → 用 JobScheduler
16.1 AlarmManager + Notification:精确到秒的定时通知
AlarmManager 是 Android 最老牌的定时工具。它能在指定时间点触发一个 PendingIntent,哪怕 App 被杀死、手机在休眠。
说白了,它就是用来做「闹钟」的。
我的经验:我在项目中做过一个「服药提醒」功能,用户设定早上8:00吃药,必须准时弹出通知。这种场景只能用 AlarmManager,因为 WorkManager 可能会因为省电策略延迟几分钟。
基本用法
// 获取 AlarmManager 实例
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// 创建通知的 Intent
Intent intent = new Intent(this, AlarmReceiver.class);
intent.putExtra("title", "吃药提醒");
intent.putExtra("content", "该吃降压药了!");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// 设置定时:早上8:00
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
// 如果当前时间已过8点,则设置为明天
if (calendar.getTimeInMillis() < System.currentTimeMillis()) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
// 设置精确闹钟(API 19+ 推荐使用 setExact)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
pendingIntent
);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
在 BroadcastReceiver 中弹出通知
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String title = intent.getStringExtra("title");
String content = intent.getStringExtra("content");
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(context, "alarm_channel")
.setSmallIcon(R.drawable.ic_alarm)
.setContentTitle(title)
.setContentText(content)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.build();
notificationManager.notify(1001, notification);
}
}
注意:从 Android 12 开始,AlarmManager 的精确闹钟需要申请 SCHEDULE_EXACT_ALARM 权限。用户可以在系统设置中关闭该权限,所以要做好降级处理。
我曾经遇到过一个线上问题:用户反馈闹钟不响,查了半天发现是国产手机把精确闹钟权限默认关了。后来我们加了权限检测和引导弹窗才解决。
16.2 WorkManager + Notification:可靠的后台定时通知
WorkManager 是 Google 官方推荐的「后台任务管理器」。它不追求精确时间,但保证任务「最终一定会执行」。
你想想看,如果你的 App 需要每隔6小时同步一次数据,然后弹出通知告知用户。这种场景用 AlarmManager 太浪费,用 JobScheduler 又太复杂。WorkManager 刚刚好。
基本用法
// 定义 Worker
public class NotificationWorker extends Worker {
public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
// 弹出通知
showNotification("数据已同步", "最新数据已更新,点击查看");
return Result.success();
}
private void showNotification(String title, String content) {
NotificationManager notificationManager =
(NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(getApplicationContext(), "sync_channel")
.setSmallIcon(R.drawable.ic_sync)
.setContentTitle(title)
.setContentText(content)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.build();
notificationManager.notify(2001, notification);
}
}
// 创建定时任务(每6小时执行一次)
PeriodicWorkRequest syncWorkRequest =
new PeriodicWorkRequest.Builder(NotificationWorker.class, 6, TimeUnit.HOURS)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 需要网络
.build())
.build();
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"sync_notification",
ExistingPeriodicWorkPolicy.KEEP,
syncWorkRequest
);
我的建议:WorkManager 的延迟时间最小是15分钟(实际可能更长)。如果你需要「半小时后弹出通知」,可以用 OneTimeWorkRequest 配合 setInitialDelay。
// 延迟30分钟后执行一次
OneTimeWorkRequest delayedWork =
new OneTimeWorkRequest.Builder(NotificationWorker.class)
.setInitialDelay(30, TimeUnit.MINUTES)
.build();
WorkManager.getInstance(context).enqueue(delayedWork);
16.3 JobScheduler + Notification:系统级省电调度
JobScheduler 是 Android 5.0 引入的系统级任务调度器。它会把多个 App 的任务合并执行,从而节省电量。
说白了,它更适合「不着急、但需要省电」的场景。比如:只在 Wi-Fi 环境下同步数据,然后弹出通知告诉用户「更新完成」。
基本用法
// 定义 JobService
public class NotificationJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 在后台线程执行任务
new Thread(() -> {
showNotification("Wi-Fi 更新", "已通过 Wi-Fi 完成数据更新");
jobFinished(params, false);
}).start();
return true; // 表示任务正在执行
}
@Override
public boolean onStopJob(JobParameters params) {
return false; // 不需要重试
}
private void showNotification(String title, String content) {
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this, "job_channel")
.setSmallIcon(R.drawable.ic_job)
.setContentTitle(title)
.setContentText(content)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setAutoCancel(true)
.build();
notificationManager.notify(3001, notification);
}
}
// 调度任务
ComponentName serviceComponent = new ComponentName(this, NotificationJobService.class);
JobInfo jobInfo = new JobInfo.Builder(1001, serviceComponent)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // 仅 Wi-Fi
.setRequiresCharging(true) // 需要充电
.setPeriodic(60 * 60 * 1000) // 每小时检查一次
.build();
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);
注意:JobScheduler 的最小周期是15分钟(实际可能更长)。而且从 Android 8.0 开始,后台执行限制更严格,JobScheduler 的触发频率会进一步降低。
我曾经在适配 Android 8.0 时踩过坑:JobScheduler 设置的1小时周期,实际可能2-3小时才触发一次。后来改用 WorkManager 才解决。
16.4 三方案对比总结
| 特性 | AlarmManager | WorkManager | JobScheduler |
|---|---|---|---|
| 精确度 | 秒级 | 分钟级(15分钟+) | 分钟级(15分钟+) |
| 省电模式兼容 | 需特殊处理 | 原生支持 | 原生支持 |
| App 被杀死后 | 仍可触发 | 仍可触发 | 仍可触发 |
| 权限要求 | SCHEDULE_EXACT_ALARM | 无特殊权限 | 无特殊权限 |
| 适用场景 | 闹钟、精确提醒 | 后台同步、定期通知 | 系统级省电调度 |
我的最终建议:
- 如果你需要「明天早上8点准时弹出通知」→ 用 AlarmManager
- 如果你需要「每6小时同步一次数据并通知用户」→ 用 WorkManager
- 如果你需要「只在 Wi-Fi 和充电时执行任务」→ 用 JobScheduler
嗯,其实大部分场景下,WorkManager 是最稳妥的选择。它兼容性好、省电、保证执行。除非你有精确到秒的需求,否则优先考虑 WorkManager。