`Notification.when` **僅控制通知列表顯示的時間戳**(供使用者查看「幾點收到」),**不控制實際發送時間**。無論設為何值,`NotificationManager.notify()` 都是**立即**彈出。
// 1. 定義接收器
public class NotificationPublisher extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// 建立 NotificationCompat.Builder ...
nm.notify(NOTIFICATION_ID, builder.build());
}
}
// 2. 設定鬧鐘(API 19+ 用 setExact,API 23+ 需請求「鬧鐘與提醒」權限)
public void scheduleNotification(Context ctx, long triggerAtMillis) {
Intent intent = new Intent(ctx, NotificationPublisher.class);
intent.putExtra("title", "定時提醒");
intent.putExtra("body", "時間到囉!");
PendingIntent pi = PendingIntent.getBroadcast(ctx, 0, intent, PendingIntent.FLAG_IMMUTABLE);
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pi);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, pi);
}
}
OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(NotificationWorker.class)
.setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
.build();
WorkManager.getInstance(ctx).enqueue(work);
啟動 `Service.startForeground()` 顯示持續通知,內部用 `ScheduledExecutorService` 定時發送。缺點:常駐記憶體、耗電、Android 8+ 需 `FOREGROUND_SERVICE` 權限。
| Android 版本 | 關鍵 API | 注意事項 |
|---|---|---|
| 5.0-5.1 | `setExact` | Doze 模式未引入,準確度高 |
| 6.0-7.1 | `setExactAndAllowWhileIdle` | Doze 維護窗口內批次執行 |
| 8.0+ | NotificationChannel + `setExactAndAllowWhileIdle` | 需建立 Channel,`IMPORTANCE_HIGH` 才會彈出浮動 |
| 12+ (API 31) | `SCHEDULE_EXACT_ALARM` 權限 | 用戶需在設定手動授予,否則 `setExact` 失效退化為 `setWindow` |
# 強制進入 Doze 測試
adb shell dumpsys deviceidle force-idle
# 查看已設定鬧鐘
adb shell dumpsys alarm | grep your.package.name
忘掉 `Notification.when` 控制發送時間。用 `AlarmManager.setExactAndAllowWhileIdle` 做精確定時,用 `WorkManager` 做彈性延遲,配合 `NotificationChannel` 確保 Android 8+ 正常彈出。