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

Android Java实现应用关闭状态下每日推送通知问题求解

Android 应用关闭状态下每日推送通知失效修复方案

现有代码存在多处兼容性、逻辑错误,会直接导致应用退到后台/被关闭后闹钟无法触发、通知无法弹出,按以下步骤修复即可。

核心问题点

  • Manifest中广播接收器配置错误:配置的无效intent-filter无实际作用,未配置开机重启监听,设备重启后闹钟会直接失效。
  • AlarmManager使用不符合高版本系统要求:setRepeating在Android 4.4以上不保证精准触发,Android 6.0以上Doze模式下会被拦截,且未适配Android 12+的PendingIntent可变性规则,会直接导致PendingIntent创建失败。
  • 闹钟时间逻辑缺陷:未判断设置的时间是否早于当前时间,若当前时间已过当日设定的推送时间,闹钟不会按预期触发。
  • 通知渠道逻辑冗余:每次触发广播才创建通知渠道,初始化Builder时使用了未创建的默认渠道ID,会导致高版本系统下通知不显示。
  • 未适配国产ROM后台限制:小米、华为、OPPO、vivo等定制ROM默认禁止应用后台自启动、后台运行,未引导用户开启对应权限的情况下,应用被杀后无法唤醒。

修复步骤

1. 修正AndroidManifest.xml配置

首先添加必要权限,修正广播接收器配置:

<!-- 基础权限 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Android13+ 通知权限,需要动态申请 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
    ...>
    <!-- 其他组件配置 -->
    <receiver
        android:name=".dailyNotification.Notification_reciever"
        android:exported="false">
        <intent-filter>
            <!-- 监听开机完成事件,重启后自动恢复闹钟 -->
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
</application>

注意:国产ROM的自启动、后台无限制权限无法通过代码申请,需要在应用内做引导,让用户手动开启,否则应用被系统强制停止后,任何定时任务都无法触发。

2. 修正闹钟设置逻辑

废弃setRepeating方法,使用适配高版本的精准闹钟API,处理时间过期问题,给PendingIntent添加正确的Flag:

private static final int NOTIFICATION_REQ_CODE = 100;

private void setDailyPushAlarm() {
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, 22);
    calendar.set(Calendar.MINUTE, 20);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);

    // 当前时间已过当日推送点,顺延到次日
    if (Calendar.getInstance().after(calendar)) {
        calendar.add(Calendar.DAY_OF_MONTH, 1);
    }

    Intent intent = new Intent(getApplicationContext(), Notification_reciever.class);
    int flag = PendingIntent.FLAG_UPDATE_CURRENT;
    // Android12+ 必须指定PendingIntent可变性
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        flag |= PendingIntent.FLAG_IMMUTABLE;
    }
    PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), NOTIFICATION_REQ_CODE, intent, flag);

    AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    // 先取消已存在的同ID闹钟,避免重复
    alarmManager.cancel(pendingIntent);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // Android6.0+ 适配Doze模式,保证休眠状态下也能触发
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // Android4.4-6.0 使用精准闹钟
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else {
        // 4.4以下版本使用重复闹钟
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
    }
}

高版本Android不支持精准重复闹钟,需要在每次闹钟触发、推送完成后,重新调用该方法设置次日的闹钟。

3. 修正广播接收器实现

优化通知渠道创建逻辑,处理开机广播事件,触发推送后自动设置次日闹钟:

public class Notification_reciever extends BroadcastReceiver {
    public static final String CHANNEL_ID = "daily_push_channel";
    private static final int NOTIFICATION_REQ_CODE = 100;

    @Override
    public void onReceive(Context context, Intent intent) {
        // 收到开机广播,直接恢复闹钟即可,不需要发通知
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            setNextDayAlarm(context);
            return;
        }

        // 初始化通知渠道
        initNotificationChannel(context);

        // 构造点击通知跳转的PendingIntent
        Intent jumpIntent = new Intent(context, MainActivity.class);
        jumpIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        int flag = PendingIntent.FLAG_UPDATE_CURRENT;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            flag |= PendingIntent.FLAG_IMMUTABLE;
        }
        PendingIntent clickPendingIntent = PendingIntent.getActivity(context, NOTIFICATION_REQ_CODE, jumpIntent, flag);

        // 构造通知
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentTitle("Notification 1")
                .setContentText("Hello")
                .setContentIntent(clickPendingIntent)
                .setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_HIGH);

        NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        if (manager != null) {
            manager.notify((int) System.currentTimeMillis(), builder.build());
        }

        // 设置次日的闹钟
        setNextDayAlarm(context);
    }

    private void initNotificationChannel(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
        NotificationManager manager = context.getSystemService(NotificationManager.class);
        // 渠道已存在则跳过创建
        if (manager.getNotificationChannel(CHANNEL_ID) != null) return;

        AudioAttributes audioAttributes = new AudioAttributes.Builder()
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .setUsage(AudioAttributes.USAGE_ALARM)
                .build();
        NotificationChannel channel = new NotificationChannel(
                CHANNEL_ID,
                "每日通知",
                NotificationManager.IMPORTANCE_HIGH
        );
        channel.enableLights(true);
        channel.setLightColor(Color.RED);
        channel.enableVibration(true);
        channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
        manager.createNotificationChannel(channel);
    }

    private void setNextDayAlarm(Context context) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 22);
        calendar.set(Calendar.MINUTE, 20);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);

        Intent intent = new Intent(context, Notification_reciever.class);
        int flag = PendingIntent.FLAG_UPDATE_CURRENT;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            flag |= PendingIntent.FLAG_IMMUTABLE;
        }
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_REQ_CODE, intent, flag);

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(pendingIntent);
        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.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
        }
    }
}

额外兼容说明

  • Android 13(API33)及以上版本,必须在应用启动时动态申请POST_NOTIFICATIONS权限,用户授权后通知才能正常弹出。
  • 广播接收器的onReceive方法执行时间不能超过10秒,若有复杂逻辑请启动前台服务处理,否则会被系统直接杀死。
  • 如果对定时精准度要求不高,可以替换为WorkManager实现定时任务,对不同厂商ROM的兼容性更好,不需要额外处理Doze模式适配。

内容的提问来源于stack exchange,提问作者Ali Habib Haidar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:18:25