Android:setExactAndAllowWhileIdle在Idle模式下无法触发通知的解决咨询
问题描述
尝试使用AlarmManager触发通知,但设备进入Idle/Doze模式后,通知无法弹出,仅在进入应用后才会显示。
Activity 代码
val intent = Intent(applicationContext, AlarmReceiver::class.java) intent.putExtra(TITLE_EXTRA, "title") intent.putExtra(MESSAGE_EXTRA, "adb shell dumpsys deviceidle step\n") val pendingIntent = PendingIntent.getBroadcast( applicationContext, NOTIFICATION_ID, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 2 * 60000, pendingIntent )
AlarmReceiver 代码
class AlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle(intent.getStringExtra(TITLE_EXTRA)) .setContentText(intent.getStringExtra(MESSAGE_EXTRA)) .setPriority(NotificationCompat.PRIORITY_HIGH) .build() val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(NOTIFICATION_ID, builder) } }
已添加的权限
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
解决方法
1. 补全Android 8.0+通知通道配置
Android 8.0及以上系统要求必须创建通知通道,且高优先级通知需要将通道重要性设为IMPORTANCE_HIGH,否则Doze模式下通知会被系统抑制。在应用启动阶段(如Application类或MainActivity的onCreate)添加通道创建代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, "高优先级通知通道", NotificationManager.IMPORTANCE_HIGH ).apply { description = "用于Doze模式下的紧急通知" enableVibration(true) setShowBadge(true) setSound(null, null) // 可自定义提示音,或用DEFAULT_SOUND } val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.createNotificationChannel(channel) }
2. 保证BroadcastReceiver执行时的CPU唤醒
Doze模式下系统会限制CPU唤醒,BroadcastReceiver的onReceive可能因CPU休眠无法完成通知显示。可以手动获取唤醒锁确保任务完成:
class AlarmReceiver : BroadcastReceiver() { private var wakeLock: PowerManager.WakeLock? = null override fun onReceive(context: Context, intent: Intent) { // 获取唤醒锁,持有10秒足够完成通知显示 val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager wakeLock = powerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP, "YourApp:AlarmWakeLock" ) wakeLock?.acquire(10 * 1000) // 原有通知逻辑,添加DEFAULT_ALL确保提示音、震动生效 val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle(intent.getStringExtra(TITLE_EXTRA)) .setContentText(intent.getStringExtra(MESSAGE_EXTRA)) .setPriority(NotificationCompat.PRIORITY_HIGH) .setDefaults(NotificationCompat.DEFAULT_ALL) .build() val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(NOTIFICATION_ID, builder) // 释放唤醒锁 wakeLock?.release() } }
同时在Manifest中添加唤醒锁权限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
3. 动态申请SCHEDULE_EXACT_ALARM权限(Android 12+)
Android 12(API 31)及以上,仅在Manifest声明SCHEDULE_EXACT_ALARM权限不够,需要动态检查并请求:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val alarmManager = getSystemService(AlarmManager::class.java) if (!alarmManager.canScheduleExactAlarms()) { startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)) } }
4. 改用WorkManager(推荐长期稳定任务)
Google推荐使用WorkManager处理Doze模式下的调度任务,它会自动适配系统休眠策略:
- 定义Worker类:
class NotificationWorker(context: Context, params: WorkerParameters) : Worker(context, params) { override fun doWork(): Result { val title = inputData.getString(TITLE_EXTRA) val message = inputData.getString(MESSAGE_EXTRA) val builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle(title) .setContentText(message) .setPriority(NotificationCompat.PRIORITY_HIGH) .build() val notificationManager = applicationContext.getSystemService(NotificationManager::class.java) notificationManager.notify(NOTIFICATION_ID, builder) return Result.success() } }
- 调度任务:
val data = Data.Builder() .putString(TITLE_EXTRA, "title") .putString(MESSAGE_EXTRA, "adb shell dumpsys deviceidle step\n") .build() val workRequest = OneTimeWorkRequestBuilder<NotificationWorker>() .setInitialDelay(2, TimeUnit.MINUTES) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setInputData(data) .build() WorkManager.getInstance(applicationContext).enqueue(workRequest)
内容的提问来源于stack exchange,提问作者itzhak levy
相关产品推荐
相关产品推荐

