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

Android 9每日8点触发通知问题:AlarmManager与WorkManager失效求助

解决Android 9+每日8点后台通知失效问题

嘿,这个问题我太熟悉了——Android 9+对后台任务的限制比之前严格多了,你的代码在低版本正常但高版本失效,主要是因为旧的AlarmManager.setRepeating()已经不适应新的系统规则了。我给你两个靠谱的解决方案,一步步来:

问题根源

Android 8.0(API 26)引入了后台执行限制,Android 9.0进一步收紧了后台任务调度:

  • setRepeating()在高版本中不再保证精确触发,系统会批量调度以节省电量
  • 当应用被完全杀死后,普通的重复闹钟会被系统直接移除
  • 你原来的代码没有适配Android 12+的PendingIntent flag要求(必须指定FLAG_IMMUTABLE或FLAG_MUTABLE)

方案1:适配高版本的AlarmManager实现

改用AlarmManager.setExactAndAllowWhileIdle(),这是系统允许的高优先级调度,即使应用被杀死或处于低电模式也能生效。注意这个方法是单次触发,所以需要在广播接收器中重新设置下一次的闹钟。

步骤1:修改闹钟初始化代码

Intent intent = new Intent(this, AppStartReceiver.class);
// 适配Android 12+的PendingIntent flag要求
int pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    pendingIntentFlags |= PendingIntent.FLAG_IMMUTABLE;
}
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 99, intent, pendingIntentFlags);

AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
long nextTriggerTime = calendar.getTimeInMillis();

// 如果当前时间已过8点,设置为明天的8点
if (System.currentTimeMillis() > nextTriggerTime) {
    nextTriggerTime += 24 * 60 * 60 * 1000;
}

// 根据系统版本选择合适的API
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
} else {
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, nextTriggerTime, 24 * 60 * 60 * 1000, pendingIntent);
}

步骤2:在广播接收器中重置闹钟

修改你的AppStartReceiver,在触发通知后立即设置第二天的闹钟:

public class AppStartReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 执行通知逻辑
        sendDailyNotification(context);
        // 重置第二天的闹钟
        resetDailyAlarm(context);
    }

    private void resetDailyAlarm(Context context) {
        Intent intent = new Intent(context, AppStartReceiver.class);
        int pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            pendingIntentFlags |= PendingIntent.FLAG_IMMUTABLE;
        }
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 99, intent, pendingIntentFlags);

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        // 设置为第二天的8点
        long nextTriggerTime = calendar.getTimeInMillis() + 24 * 60 * 60 * 1000;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
        } else {
            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, nextTriggerTime, 24 * 60 * 60 * 1000, pendingIntent);
        }
    }

    private void sendDailyNotification(Context context) {
        // Android 8.0+必须创建通知渠道
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("daily_alarm", "每日通知", NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "daily_alarm")
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle("每日提醒")
                .setContentText("现在是上午8点啦")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);

        notificationManager.notify(1, builder.build());
    }
}

步骤3:清单文件配置广播接收器

确保在AndroidManifest.xml中正确注册接收器:

<receiver
    android:name=".AppStartReceiver"
    android:exported="false">
</receiver>

方案2:使用WorkManager(更推荐的后台调度方式)

WorkManager是Google官方推荐的后台任务调度方案,自动适配所有系统版本,即使应用被杀死或设备重启也能执行任务。

步骤1:创建Worker类

public class DailyAlarmWorker extends Worker {
    public DailyAlarmWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        // 发送每日通知
        sendDailyNotification(getApplicationContext());
        return Result.success();
    }

    private void sendDailyNotification(Context context) {
        // 和广播接收器中的通知逻辑一致
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("daily_alarm", "每日通知", NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "daily_alarm")
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle("每日提醒")
                .setContentText("现在是上午8点啦")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);

        notificationManager.notify(1, builder.build());
    }
}

步骤2:调度每日任务

在应用启动时(比如Application的onCreate或主页面的onCreate)提交任务:

// 计算到下一个8点的延迟时间
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
long now = System.currentTimeMillis();
long initialDelay = calendar.getTimeInMillis() - now;
if (initialDelay < 0) {
    // 当前时间已过8点,延迟到明天的8点
    initialDelay += 24 * 60 * 60 * 1000;
}

// 创建周期性任务,间隔24小时
PeriodicWorkRequest dailyWorkRequest = new PeriodicWorkRequest.Builder(DailyAlarmWorker.class, 24, TimeUnit.HOURS)
        .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
        .setConstraints(new Constraints.Builder()
                .setRequiresDeviceIdle(false) // 不需要设备空闲,确保到点触发
                .setRequiresBatteryNotLow(false) // 允许低电模式下运行
                .build())
        .setPersisted(true) // 设备重启后仍会执行
        .build();

// 提交唯一任务,避免重复创建
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
        "DailyAlarm",
        ExistingPeriodicWorkPolicy.REPLACE,
        dailyWorkRequest
);

关键注意事项

  • PendingIntent Flag:Android 12+必须指定FLAG_IMMUTABLE或FLAG_MUTABLE,否则会抛出异常
  • 通知渠道:Android 8.0+必须创建通知渠道才能发送通知
  • 后台权限:Android 13+需要申请POST_NOTIFICATIONS权限
  • 厂商定制系统:小米、华为等厂商的系统会额外限制后台任务,建议引导用户将应用加入电池优化白名单

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:53:31