Android 14下Alarm Receiver异常及通知失效问题求助
Android 14 定时通知崩溃问题
开发用户选定日期时间弹出通知功能时,在Android 14设备上遇到错误:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
添加FLAG_ACTIVITY_NEW_TASK标记后,通知仍无法正常弹出。该功能在其他Android版本设备(包括应用被杀死状态)均能正常显示,仅Android 14上崩溃。
相关代码
AlarmReceiver.kt
class AlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if ("android.intent.action.BOOT_COMPLETED" == intent.action) { AlarmUtil.scheduleAll(context) return } val dayOfWeek = intent.getIntExtra("Day", 2) AlarmUtil.scheduleAlarm(context, dayOfWeek, AppPreferences.hourOfDay, AppPreferences.minuteOfDay, true) val notificationId = Random().nextInt(60000) val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager val channelId = App.instance.applicationContext.getString(R.string.message_notification_channel_id) val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = App.instance.applicationContext.getString(R.string.message_notification_channel_name) val descriptionText = App.instance.applicationContext.getString(R.string.message_notification_channel_name) val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel(channelId, name, importance).apply { description = descriptionText enableLights(true) enableVibration(true) if (defaultSoundUri != null) { val att = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build() setSound(defaultSoundUri, att) } lightColor = Color.parseColor("#501450") } notificationManager.createNotificationChannel(channel) } val newIntent = Intent(context, StartActivity::class.java) newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) newIntent.action = "actionString" + System.currentTimeMillis() val pendingIntent = PendingIntent.getActivity(context, 0, newIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) val notificationBuilder = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_notification)//a resource for your custom small icon .setColor(Color.parseColor("#222831")) .setContentText(App.instance.applicationContext.getString(R.string.remiderNotificationText))//ditto .setAutoCancel(true) //dismisses the notification on click .setSound(defaultSoundUri) .setVibrate(LongArray(0)) .setDefaults(NotificationCompat.DEFAULT_ALL) .setPriority(NotificationCompat.PRIORITY_MAX) // heads-up .setContentIntent(pendingIntent) notificationManager.notify( notificationId /* ID of notification */, notificationBuilder.build() ) } }
AlarmUtil.kt
object AlarmUtil { private val alarmManager = App.instance.applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager fun scheduleAlarm(context: Context?, dayOfWeek: Int, hourOfTheDay: Int, minuteOfTheDay: Int, repeating: Boolean) { val calendar = Calendar.getInstance() when(dayOfWeek){ 1 -> calendar[Calendar.DAY_OF_WEEK] = Calendar.MONDAY 2 -> calendar[Calendar.DAY_OF_WEEK] = Calendar.TUESDAY 3 -> calendar[Calendar.DAY_OF_WEEK] = Calendar.WEDNESDAY 4 -> calendar[Calendar.DAY_OF_WEEK] = Calendar.THURSDAY 5 -> calendar[Calendar.DAY_OF_WEEK] = Calendar.FRIDAY 6 -> calendar[Calendar.DAY_OF_WEEK] = Calendar.SATURDAY 7 -> calendar[Calendar.DAY_OF_WEEK] = Calendar.SUNDAY } calendar[Calendar.HOUR_OF_DAY] = hourOfTheDay calendar[Calendar.MINUTE] = minuteOfTheDay calendar[Calendar.SECOND] = 0 if(repeating || calendar.timeInMillis < System.currentTimeMillis()){ calendar.add(Calendar.DATE, 7) } val intent = Intent(context, AlarmReceiver::class.java) intent.putExtra("Day", dayOfWeek) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val pendingIntent = PendingIntent.getBroadcast(context, dayOfWeek, intent, PendingIntent.FLAG_IMMUTABLE) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent) return } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (!alarmManager.canScheduleExactAlarms()) { Intent().also { it.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) context?.startActivity(it) } }else { alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent) } } } fun cancelAllAlarms(context: Context?) { val intent = Intent(context, AlarmReceiver::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) AppPreferences.selectedDays.fromPrettyJson<Array<String>>().forEach { val pendingIntent = PendingIntent.getBroadcast(context, it.toInt(), intent, PendingIntent.FLAG_IMMUTABLE) alarmManager.cancel(pendingIntent) } } fun scheduleAll(context: Context?) { if(AppPreferences.userNotifications && AppPreferences.specialUserNotifications){ AppPreferences.selectedDays.fromPrettyJson<ArrayList<String>>().forEach { scheduleAlarm(context, it.toInt(), AppPreferences.hourOfDay, AppPreferences.minuteOfDay, false) } } } }
Reminder相关类
class ReminderService : Service() { override fun onBind(p0: Intent?): IBinder? { return null } override fun onTaskRemoved(rootIntent: Intent) { super.onTaskRemoved(rootIntent) if(AppPreferences.selectedDays.isNotEmpty()){ if(AppPreferences.specialUserNotifications) { AlarmUtil.scheduleAll(applicationContext) } else { AlarmUtil.cancelAllAlarms(applicationContext) } } this.stopSelf() } } class ReminderWorker(appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) { override suspend fun doWork(): Result { // Check if app is in foreground if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { val intent = Intent(applicationContext, ReminderService::class.java) applicationContext.startService(intent) } return Result.success() } } // 主Activity中代码 val workRequest = OneTimeWorkRequestBuilder<ReminderWorker>().build() // Enqueue the work request WorkManager.getInstance(this).enqueue(workRequest)
修复方案
1. 移除广播Intent的错误标记
FLAG_ACTIVITY_NEW_TASK是Activity启动标记,广播Intent不需要添加,直接删除AlarmUtil中对应代码:
// 错误代码,删除这行 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
2. 优化通知PendingIntent配置
Android 14对后台启动Activity限制更严格,调整标记并使用唯一requestCode避免Intent冲突:
val newIntent = Intent(context, StartActivity::class.java) // 保留必要标记,移除冲突的CLEAR_TASK newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) // 用唯一的notificationId作为requestCode val pendingIntent = PendingIntent.getActivity( context, notificationId, newIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT )
3. 修复通知渠道创建逻辑
避免重复创建渠道,且修正描述文本引用错误:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val existingChannel = notificationManager.getNotificationChannel(channelId) if (existingChannel == null) { val name = context.getString(R.string.message_notification_channel_name) val descriptionText = context.getString(R.string.message_notification_channel_description) val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel(channelId, name, importance).apply { description = descriptionText enableLights(true) enableVibration(true) defaultSoundUri?.let { val att = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build() setSound(it, att) } lightColor = Color.parseColor("#501450") } notificationManager.createNotificationChannel(channel) } }
4. 适配Android 14后台Service启动规则
改用前台Service启动方式,避免后台启动限制:
// ReminderWorker中修改 val intent = Intent(applicationContext, ReminderService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { applicationContext.startForegroundService(intent) } else { applicationContext.startService(intent) } // ReminderService中添加前台通知逻辑 override fun onCreate() { super.onCreate() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId = "reminder_service_channel" val channel = NotificationChannel( channelId, "Reminder Service", NotificationManager.IMPORTANCE_LOW ) getSystemService(NotificationManager::class.java).createNotificationChannel(channel) val notification = NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("后台提醒服务") .setContentText("服务运行中") .build() startForeground(1001, notification) } }
5. 确保精确闹钟权限正常申请
在应用启动页或设置页主动检查权限,引导用户授权:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager if (!alarmManager.canScheduleExactAlarms()) { startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)) } }
内容的提问来源于stack exchange,提问作者flutra
相关产品推荐
相关产品推荐

