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

手机锁屏状态下如何在指定时间执行动作?现有方法失效求助

解决Android锁屏后定时通知不触发的问题

这个问题我太熟了!Android 6.0以后引入的Doze模式是核心原因——当设备锁屏一段时间进入低功耗状态后,系统会限制普通定时任务的执行,直到设备亮屏或者退出Doze模式。要实现指定时间精准触发,得针对性调整你的定时任务实现方式:

核心解决方案:使用AlarmManager的Doze兼容API

普通的AlarmManager.set()方法在Doze模式下会被延迟,我们需要改用支持Doze模式的精确闹钟API,确保系统即使在低功耗状态也会唤醒执行任务。

1. 修改你的startBroadcast方法

替换原来的Calendar+普通闹钟逻辑,改成以下代码:

public void startBroadcast(int minutes, int hours) {
    Calendar calendar = Calendar.getInstance();
    // 设置目标触发时间(今天的hours:minutes)
    calendar.set(Calendar.HOUR_OF_DAY, hours);
    calendar.set(Calendar.MINUTE, minutes);
    calendar.set(Calendar.SECOND, 0);
    
    // 如果设置的时间已经过了,自动顺延到明天同一时间
    if (calendar.getTimeInMillis() < System.currentTimeMillis()) {
        calendar.add(Calendar.DAY_OF_YEAR, 1);
    }

    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(this, AlarmReceiver.class);
    // Android 12+必须指定IMMUTABLE/MUTABLE flag,这里用IMMUTABLE更安全
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
            this, 
            0, 
            intent, 
            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );

    // 根据系统版本选择合适的闹钟API
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // 支持Doze模式的精确闹钟,最推荐的方式
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // 4.4+的精确闹钟(不支持Doze,但比普通set更精准)
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    } else {
        // 低版本系统用普通闹钟
        alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    }
}

2. 实现广播接收器处理通知

创建一个专门的BroadcastReceiver来接收闹钟触发事件并弹出通知,注意Android 8.0+必须创建通知渠道才能显示通知:

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Android 8.0+ 创建通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    "TIMED_NOTIFICATION",
                    "定时通知",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }

        // 构建通知内容
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "TIMED_NOTIFICATION")
                .setSmallIcon(R.drawable.ic_notification) // 替换成你的通知图标
                .setContentTitle("定时提醒")
                .setContentText("到你设置的时间啦!")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true); // 点击后自动取消通知

        // 显示通知
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
        notificationManager.notify(1, builder.build());

        // 如果需要重复触发,在这里重新调用startBroadcast设置下一次闹钟
        // 比如每天同一时间触发:startBroadcast(minutes, hours);
    }
}

3. 必要的配置与权限

  • AndroidManifest.xml中注册广播接收器:
<receiver android:name=".AlarmReceiver" />
  • Android 13+ 需要动态申请通知权限,在Activity中添加:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
        ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestPermission(),
                isGranted -> {
                    if (isGranted) {
                        // 权限已授予,可以发送通知
                    }
                }
        );
        requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
    }
}

关键注意事项

  • 不要使用HandlerTimer等本地定时工具:锁屏后App可能被系统挂起,这些工具会失效。
  • 重复任务处理:setExactAndAllowWhileIdle()不支持重复闹钟,所以需要在每次触发后重新设置下一次的闹钟时间。
  • 测试时的小技巧:可以用开发者选项中的“强制进入Doze模式”来快速测试锁屏后的触发效果,不用等系统自动进入Doze。

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

火山引擎 最新活动