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

使用Coroutine调用Service设置AlarmManager后应用每10秒崩溃

问题根源与修复方案

你的应用运行10秒后崩溃,核心原因是:调用ContextCompat.startForegroundService()启动服务后,未在10秒内调用startForeground()显示前台通知——这是Android系统对前台服务的强制要求,违反此规则会被系统直接杀死服务,导致应用崩溃。

以下是具体修复步骤:

1. 给AlarmService添加前台通知

修改AlarmService,在启动后立即创建并显示前台通知,满足系统要求:

class AlarmService : Service() {
    private val NOTIFICATION_ID = 1001
    private val CHANNEL_ID = "ALARM_CHANNEL"

    override fun onCreate() {
        super.onCreate()
        // Android O及以上必须创建通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "闹钟服务",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "用于维持闹钟定时任务"
            }
            getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val time = intent?.getLongExtra("TimeSelected", 0) ?: 0

        // 构建前台通知
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("闹钟服务运行中")
            .setContentText("等待定时触发")
            .setSmallIcon(R.drawable.ic_notification) // 替换为你的应用通知图标
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()

        // 启动前台服务,必须调用此方法
        startForeground(NOTIFICATION_ID, notification)

        // 使用Service生命周期绑定的协程,避免内存泄漏
        lifecycleScope.launch(Dispatchers.IO) {
            Log.d("coroutine"," the thread is working in service")
            setAlarm(time)
            // 闹钟设置完成后,可停止前台并销毁服务(无需持续运行)
            stopForeground(STOP_FOREGROUND_REMOVE)
            stopSelf(startId)
        }

        return START_NOT_STICKY
    }

    private fun setAlarm(time: Long) {
        val alarmManager: AlarmManager? = getSystemService(Context.ALARM_SERVICE) as? AlarmManager
        Log.d("coroutine"," the setalarm method is working in service")
        val alarmIntent = Intent(this, AlarmBroadcastReceiver::class.java)

        // 适配Android 12+的PendingIntent Flag
        val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
        } else {
            PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)
        }

        alarmManager?.set(AlarmManager.RTC_WAKEUP, time - 3, pendingIntent)
    }

    override fun onBind(p0: Intent?): IBinder? = null
}

2. 替换自定义协程Scope为LifecycleCoroutineScope

原代码中使用CoroutineScope(Dispatchers.IO).launch会导致内存泄漏,改用lifecycleScope(绑定Service生命周期,销毁时自动取消协程)。需确保添加依赖:

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" // 版本按需调整

3. 修复BroadcastReceiver的MediaPlayer资源泄漏

原代码中MediaPlayer播放后未释放资源,修改onReceive方法:

class AlarmBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, p1: Intent?) {
        try {
            Log.d("coroutine", " the broadcast receiver is called")
            Toast.makeText(context, "it is working", Toast.LENGTH_SHORT).show()

            val mediaPlayer = MediaPlayer.create(context, Settings.System.DEFAULT_RINGTONE_URI)
            mediaPlayer.start()
            // 播放完毕后释放资源
            mediaPlayer.setOnCompletionListener { mp ->
                mp.release()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e("YourReceiver", "Exception in onReceive: ${e.message}")
        }
    }
}

关键说明

  • 前台服务必须显示通知,这是Android系统的硬性规则,目的是让用户知晓后台有服务在运行。
  • 闹钟设置完成后可停止前台服务,因为AlarmManager已经注册了定时任务,无需Service持续运行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 09:51:24