You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android不依赖Firebase实现指定时间触发用户提醒通知方法

Android 无Firebase依赖的离线定时提醒实现方案

别用你现在写的主动轮询算时间差的方案——Android系统对后台进程有严格限制,应用切后台、被用户划掉之后,你的代码根本没机会持续运行检测时间,不仅费电,触发率极低。另外你写的minutes == 60这类精确值判断逻辑也不可靠,只要检测时间点偏移几秒就会错过触发条件。

正确的实现思路完全不需要你自己持续检测时间:用户创建项目选好提醒档位时,直接把每个提醒节点的触发时间算好,交给系统级的AlarmManager服务排期,到点系统会自动唤醒你的应用发通知,哪怕应用进程已经被杀也能正常触发。


具体实现步骤

1. 预计算所有提醒节点的触发时间

用户保存项目、勾选需要的提醒档位(2周/1周/1天/1小时/30分钟)时,直接基于项目截止时间往前偏移对应时长,算出每个提醒的精确触发时间,转成系统时区的毫秒级时间戳,同时给每个提醒生成全局唯一的ID,方便后续取消/重设任务。

// 提醒档位对应的分钟偏移量
int[] remindOffsetMinutes = {20160, 10080, 1440, 60, 30};
// 遍历用户选中的提醒档位
for (int offset : userSelectedOffsets) {
    LocalDateTime remindTriggerTime = dateDue.minusMinutes(offset);
    // 转成系统时区的毫秒时间戳,供AlarmManager使用
    long triggerAtMillis = remindTriggerTime
            .atZone(ZoneId.systemDefault())
            .toInstant()
            .toEpochMilli();
    // 生成唯一任务ID:可以用项目ID+偏移量拼接生成int值,全局唯一即可
    int taskId = buildUniqueTaskId(projectId, offset);
    // 调用方法设置系统定时任务
    scheduleRemindAlarm(triggerAtMillis, taskId, projectId, projectTitle);
}

2. 配置AlarmManager系统定时任务

注意适配不同Android版本的API差异和权限要求,确保Doze休眠模式下也能正常触发:

  • Android 12及以上需要先申请SCHEDULE_EXACT_ALARM精确闹钟权限
  • Android 6.0及以上使用setExactAndAllowWhileIdle方法,可绕过系统Doze休眠限制,到点必触发
  • Android 6.0以下使用setExact方法设置精确闹钟
private void scheduleRemindAlarm(long triggerAtMillis, int taskId, long projectId, String projectTitle) {
    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    Intent intent = new Intent(this, RemindPushReceiver.class);
    // 把项目相关信息塞进Intent,触发时取出用于构建通知
    intent.putExtra("projectId", projectId);
    intent.putExtra("projectTitle", projectTitle);

    PendingIntent pendingIntent = PendingIntent.getBroadcast(
            this,
            taskId,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );

    // 分版本适配设置闹钟
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        if (alarmManager.canScheduleExactAlarms()) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
        } else {
            // 可在此处引导用户开启精确闹钟权限
        }
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
    } else {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
    }
}

3. 实现广播接收器接收触发回调

写一个继承BroadcastReceiver的类,在onReceive回调中取出传递的项目信息,构建并推送通知即可。哪怕应用进程未启动,系统触发闹钟时也会临时拉起你的应用进程执行这段逻辑,不需要维持后台常驻。

public class RemindPushReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        long projectId = intent.getLongExtra("projectId", -1);
        String projectTitle = intent.getStringExtra("projectTitle");
        NotificationManager notifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        // Android 8.0及以上必须创建通知渠道,否则通知不显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    "project_remind",
                    "项目截止提醒",
                    NotificationManager.IMPORTANCE_HIGH
            );
            notifyManager.createNotificationChannel(channel);
        }

        // 构建通知
        Notification notification = new NotificationCompat.Builder(context, "project_remind")
                .setContentTitle("项目截止提醒")
                .setContentText(String.format("项目「%s」即将到达截止时间", projectTitle))
                .setSmallIcon(R.drawable.ic_remind_icon)
                .setAutoCancel(true)
                .build();

        notifyManager.notify((int) projectId, notification);
    }
}

4. 清单文件配置权限和组件

在AndroidManifest.xml中添加必要权限,注册广播接收器:

<!-- 必要权限 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<!-- 开机广播权限,用于重启后恢复闹钟任务 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<!-- 注册提醒接收器 -->
<receiver
    android:name=".RemindPushReceiver"
    android:exported="false" />
<!-- 注册开机广播接收器,重启后重新设置所有待触发的闹钟 -->
<receiver
    android:name=".BootCompleteReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

注意事项

  • 如果用户后续修改了项目截止时间、或者删除了项目,要通过对应的任务ID拿到之前的PendingIntent,调用alarmManager.cancel(pendingIntent)取消旧任务,避免错发提醒
  • 不要用WorkManager、JobScheduler实现这类精确时间点的提醒,这两个API是系统根据设备状态批量调度任务的,会存在时间偏差,不适合到点必须触发的场景
  • 不要写后台Service常驻轮询时间,Android现在对后台Service限制极严,大概率会被系统杀死,还会额外消耗电量
  • Android 13及以上需要动态申请POST_NOTIFICATIONS通知权限,否则通知无法正常展示
  • 设备重启后系统会清空所有已设置的闹钟,需要在开机广播里读取本地存储的所有待提醒项目,重新调用设置闹钟的方法恢复任务

内容的提问来源于stack exchange,提问作者ServletException

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 20:36:28