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

Android AlarmManager新增闹钟无法设置或触发问题排查求助

问题根因与修复方案

核心问题点

  • PendingIntent 未指定可变性标识(适配安卓12+):API 31及以上系统要求PendingIntent必须显式指定FLAG_IMMUTABLE或FLAG_MUTABLE,你当前传入的0属于非法参数,系统会直接丢弃该PendingIntent,导致闹钟设置失效,同时isReminderSet方法查询时也会返回null。
  • 缺失必要权限:
    1. API 31及以上设置精确闹钟需要android.permission.SCHEDULE_EXACT_ALARM权限,未声明该权限会导致闹钟设置直接失败
    2. 接收开机广播需要声明android.permission.RECEIVE_BOOT_COMPLETED权限,你当前Manifest中未配置该权限,设备重启后闹钟无法重新注册
  • 闹钟调用逻辑重复:API 31的设备会先后执行setAndAllowWhileIdle和setExactAndAllowWhileIdle两次调用,后者会覆盖前者的配置,且你不需要额外调用setAndAllowWhileIdle,该方法用于不精确提醒场景
  • 次日调度逻辑不合理:当前逻辑只要设定时间与当前时间差小于30分钟就直接调度到次日,会导致用户在接近提醒时间开启功能时,当天无法收到提醒,误以为功能失效
  • 时区同步问题:LocalTime使用设备当前时区,但未处理用户时区变更的场景,会导致闹钟时间偏移

修复方案

1. 补充Manifest权限与配置

<!-- 开机广播权限 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 安卓12-13精确闹钟权限 -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:maxSdkVersion="33" />
<!-- 安卓14+精确闹钟权限 -->
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />

<receiver android:name="com.lostpixels.biblept.notifications.AlarmReceiver" android:enabled="true"/>
<receiver android:name="com.lostpixels.biblept.notifications.BootReceiver" android:enabled="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
        <action android:name="android.intent.action.TIMEZONE_CHANGED"/>
        <action android:name="android.intent.action.TIME_SET"/>
    </intent-filter>
</receiver>

2. 修复PendingIntent Flag与闹钟设置逻辑

fun scheduleReminder(context: Context, reschedule: Boolean = false) {
    val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager
    // 移除lazy,每次创建PendingIntent,指定正确的Flag
    val intent = Intent(context, AlarmReceiver::class.java)
    val alarmPendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
    } else {
        PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    }

    val time = getReminderTime(context = context)
    val now = LocalTime.now()
    // 修复次日判断逻辑:只有当前时间已经过了设定时间,才设到次日
    val shouldSetForTomorrow = time < now || reschedule
    val calendar = GregorianCalendar.getInstance().apply {
        if (shouldSetForTomorrow) {
            add(Calendar.DAY_OF_MONTH, 1)
        }
        set(Calendar.HOUR_OF_DAY, time.hour)
        set(Calendar.MINUTE, time.minute)
        set(Calendar.SECOND, 0)
        set(Calendar.MILLISECOND, 0)
    }

    // 修正版本判断逻辑,避免重复调用
    when {
        Build.VERSION.SDK_INT >= 23 -> {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, alarmPendingIntent)
        }
        else -> {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, alarmPendingIntent)
        }
    }
}

3. 修复isReminderSet方法的Flag

fun isReminderSet(context: Context): Boolean {
    val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
    } else {
        PendingIntent.FLAG_NO_CREATE
    }
    return PendingIntent.getBroadcast(
        context, 0,
        Intent(context, AlarmReceiver::class.java),
        flag
    ) != null
}

4. 新增时区/时间变更监听

在BootReceiver中新增对时间、时区变更的监听,收到广播后重新设置闹钟,避免时间变更导致的提醒偏移:

class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_BOOT_COMPLETED 
            || intent.action == Intent.ACTION_TIMEZONE_CHANGED
            || intent.action == Intent.ACTION_TIME_SET) {
            if (userWantsReminder(context)) {
                scheduleReminder(context = context)
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 18:54:02