「已注销」 2025-12-02 18:04 采纳率: 25%
浏览 2

AlarmManager问题

#闹钟设置后不回调

调用界面


class AlarmClockActivity : BaseActivity() {

    override fun getLayoutResId(): Int {
        return R.layout.act_alarm_clock
    }

    override fun initActivity() {
        setAlarm( this)
    }

    // TODO: 2025/12/2 16:03 -(Administrator)-{🎉  内部调用}

    fun setAlarm(context: Context) {
        val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

        // 检查并请求精确闹钟权限 (Android 12+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            if (!alarmMgr.canScheduleExactAlarms()) {
                // 跳转到权限请求页面
                val intent = Intent(ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
                startActivity(intent)
                return
            }
        }

        val calendar: Calendar = Calendar.getInstance().apply {
            timeInMillis = System.currentTimeMillis()
            set(Calendar.HOUR_OF_DAY, 18)
            set(Calendar.MINUTE, 1)
        }

        // 确保闹钟设置在未来时间
        if (calendar.timeInMillis <= System.currentTimeMillis()) {
            calendar.add(Calendar.DAY_OF_YEAR, 1)
        }

        val pendingIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
            PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
        }

        // 根据Android版本选择正确的方法
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmMgr.setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        } else {
            alarmMgr.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        }
    }

    // TODO: 2025/12/2 16:13 -(Administrator)-{🎉  全局调用}

    companion object{
        fun startActivity(context: Context){
            val intent = Intent(context, AlarmClockActivity::class.java)
            context.startActivity(intent)
        }
    }
}

回调界面


class AlarmReceiver : BroadcastReceiver(){

    override fun onReceive(context: Context?, intent: Intent?) {
        Toast.makeText(context, "闹钟响了!", Toast.LENGTH_LONG).show()
    }
}
  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-12-02 18:11
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    你提到的问题是:设置闹钟后,AlarmReceiver 没有被回调。这是一个比较常见的问题,特别是在 Android 12 及以上版本中,由于系统对后台任务的限制,可能会导致 BroadcastReceiver 无法正常工作。


    原因分析

    1. 权限问题

      • 在 Android 12(API 31)及以上版本中,如果使用了 setExactAndAllowWhileIdle() 方法,需要用户明确授予“精确闹钟权限”。
      • 如果没有获得该权限,即使代码逻辑正确,AlarmManager 也不会触发广播。
    2. BroadcastReceiver 注册方式不正确

      • 你目前在 AndroidManifest.xml 中是否注册了 AlarmReceiver?如果没有注册,系统将不会接收到广播。
      • 在 Android 8.0(API 26)及以上版本中,隐式广播(Implicit Broadcasts) 被限制,必须显式注册 BroadcastReceiver
    3. PendingIntent 的 Flags 设置不当

      • FLAG_IMMUTABLE 是 Android 12 引入的新标志,表示这个 PendingIntent 不可修改。如果你使用的是旧版本 SDK,可能需要使用 FLAG_UPDATE_CURRENT 或其他标志。
    4. 系统休眠/省电模式影响

      • 部分手机厂商(如小米、华为等)的省电模式会阻止后台服务运行,导致 AlarmManager 无法唤醒设备。

    🔧 解决方案

    1. 确保已请求并获得“精确闹钟权限”

    setAlarm() 方法中,你已经检查了 alarmMgr.canScheduleExactAlarms(),但还需要处理用户跳转到权限页面后的结果。

    修改 setAlarm() 方法:

    fun setAlarm(context: Context) {
        val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    
        // 检查并请求精确闹钟权限 (Android 12+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            if (!alarmMgr.canScheduleExactAlarms()) {
                // 跳转到权限请求页面
                val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
                context.startActivity(intent)
                return
            }
        }
    
        // ... 其余代码保持不变 ...
    }
    

    ⚠️ 注意:ACTION_REQUEST_SCHEDULE_EXACT_ALARM 是一个系统常量,请确认你的项目中是否包含它,或者直接使用字符串 "android.settings.REQUEST_SCHEDULE_EXACT_ALARM"


    2. AndroidManifest.xml 中注册 AlarmReceiver

    你需要在 AndroidManifest.xml 中显式注册 AlarmReceiver,否则系统不会调用它。

    <receiver
        android:name=".AlarmReceiver"
        android:enabled="true"
        android:exported="true" />
    

    android:exported="true" 表示允许外部应用调用该接收器(例如通过 AlarmManager)。


    3. 调整 PendingIntent 的 Flags

    根据 Android 版本,适当调整 PendingIntent.FLAG_IMMUTABLEPendingIntent.FLAG_UPDATE_CURRENT

    修改 setAlarm() 中的 pendingIntent 创建部分:

    val pendingIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
        } else {
            PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
        }
    }
    

    4. 测试时关闭省电模式或加入白名单

    • 手动关闭省电模式:进入手机设置 → 电池 → 省电模式,关闭。
    • 加入白名单:在系统设置中,将你的应用加入“忽略电池优化”列表。

    5. 添加日志进行调试

    onReceive() 方法中添加日志,方便排查是否真的没有被调用:

    class AlarmReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context?, intent: Intent?) {
            Log.d("AlarmReceiver", "onReceive called!")
            Toast.makeText(context, "闹钟响了!", Toast.LENGTH_LONG).show()
        }
    }
    

    然后在 Android Studio 中查看 Logcat 日志,确认 onReceive() 是否执行。


    📝 总结

    | 步骤 | 内容 | |------|------| | 1 | 检查并请求“精确闹钟权限”,并在 AndroidManifest.xml 中注册 AlarmReceiver | | 2 | 调整 PendingIntent 的 flags,适配不同 Android 版本 | | 3 | 关闭省电模式或加入白名单 | | 4 | 添加日志用于调试 |


    🧪 示例修改后的完整代码片段

    修改后的 setAlarm() 方法:

    fun setAlarm(context: Context) {
        val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    
        // 检查并请求精确闹钟权限 (Android 12+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            if (!alarmMgr.canScheduleExactAlarms()) {
                // 跳转到权限请求页面
                val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
                context.startActivity(intent)
                return
            }
        }
    
        val calendar: Calendar = Calendar.getInstance().apply {
            timeInMillis = System.currentTimeMillis()
            set(Calendar.HOUR_OF_DAY, 18)
            set(Calendar.MINUTE, 1)
        }
    
        // 确保闹钟设置在未来时间
        if (calendar.timeInMillis <= System.currentTimeMillis()) {
            calendar.add(Calendar.DAY_OF_YEAR, 1)
        }
    
        val pendingIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
            } else {
                PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
            }
        }
    
        // 根据Android版本选择正确的方法
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmMgr.setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        } else {
            alarmMgr.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
        }
    }
    

    🧩 最终建议

    • 如果你仍然无法解决问题,请提供 AndroidManifest.xml 文件内容,以便进一步排查。
    • 如果你是开发人员,可以尝试在模拟器上测试,避免手机厂商的定制系统干扰。

    如有更多问题,欢迎继续提问!

    评论

报告相同问题?

问题事件

  • 创建了问题 12月2日