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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 16:05:54