使用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
相关产品推荐
相关产品推荐

