You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android:如何在指定时长后仅触发一次通知?

解决Android预算应用的单次定时通知问题

我来帮你搞定这个定时通知的需求!你之前尝试用TimerCalendar没成功,核心问题在于Timer依赖应用进程存活,一旦App退到后台被系统回收,定时任务就会失效;而且你当前的代码直接在onCreate里调用displayNotification,相当于打开页面就弹通知,完全没有实现“延迟指定时长后触发”的逻辑。

正确的方案是使用系统级的AlarmManager,它能在App进程不存在时依然触发定时任务,保证通知能准时弹出。下面是完整的实现步骤:

1. 创建广播接收器处理通知触发

先写一个BroadcastReceiver,它会在定时时间到的时候被系统唤醒,然后执行弹出通知的逻辑:

public class BudgetNotificationReceiver extends BroadcastReceiver {
    public static final int MY_NOTIFICATION = 1;

    @Override
    public void onReceive(Context context, Intent intent) {
        // 时间到了,弹出通知
        showExpiredNotification(context);
    }

    private void showExpiredNotification(Context context) {
        // 点击通知跳转的页面
        Intent intent = new Intent(context, SecondActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                context,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        // 构建通知(适配Android O及以上的通知渠道要求)
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "BUDGET_CHANNEL")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_alarm)
                .setTicker("Budget has ended!")
                .setAutoCancel(true)
                .setContentIntent(pendingIntent)
                .setContentTitle("The Budget App")
                .setContentText("Your budget has expired!")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);

        NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        // Android O及以上必须创建通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    "BUDGET_CHANNEL",
                    "Budget Reminders",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            manager.createNotificationChannel(channel);
        }
        manager.notify(MY_NOTIFICATION, builder.build());
    }
}

2. 在Manifest中注册广播接收器

打开AndroidManifest.xml,添加接收器的注册信息:

<receiver android:name=".BudgetNotificationReceiver" />

3. 实现定时任务的设置与重置

在你的BudgetActivity里,添加设置定时和取消定时的方法:

// 设置定时通知:delayMillis是延迟的毫秒数(比如一天=86400000L)
private void scheduleBudgetExpiry(long delayMillis) {
    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(this, BudgetNotificationReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );

    long triggerTime = System.currentTimeMillis() + delayMillis;

    // 根据Android版本选择合适的闹钟API
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        // Android 12+ 需要精确闹钟权限,先检查权限
        if (alarmManager.canScheduleExactAlarms()) {
            alarmManager.setExactAndAllowWhileIdle(
                    AlarmManager.RTC_WAKEUP,
                    triggerTime,
                    pendingIntent
            );
        } else {
            // 引导用户开启权限
            Intent permissionIntent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
            startActivity(permissionIntent);
        }
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(
                AlarmManager.RTC_WAKEUP,
                triggerTime,
                pendingIntent
        );
    } else {
        alarmManager.setExact(
                AlarmManager.RTC_WAKEUP,
                triggerTime,
                pendingIntent
        );
    }
}

// 重置预算时,取消之前的定时任务
private void cancelExistingNotification() {
    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(this, BudgetNotificationReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );
    alarmManager.cancel(pendingIntent);
}

4. 在业务逻辑中调用

比如用户选择“一天后提醒”,就调用:

// 设置一天后触发通知
scheduleBudgetExpiry(86400000L);

当用户点击“重置预算”按钮时,先取消旧的定时,再设置新的:

cancelExistingNotification();
// 重新设置新周期的定时
scheduleBudgetExpiry(86400000L);

关键注意点

  • AlarmManager是系统级服务,即使App被后台回收,依然能准时触发广播;
  • Android 12+ 需要申请SCHEDULE_EXACT_ALARM权限才能使用精确定时,代码里已经做了权限检查和引导;
  • 通知渠道是Android O及以上的强制要求,必须创建否则通知不会显示;
  • PendingIntentFLAG_IMMUTABLE是Android 12+的推荐设置,避免兼容性问题。

内容的提问来源于stack exchange,提问作者Jeremy S.

火山引擎 最新活动