在MainActivity中创建访问配合AlarmManager的BroadcastReceiver的正确方法(Kotlin)
在Activity中创建并使用供AlarmManager调用的BroadcastReceiver的正确方式
现有代码核心问题
- 构建PendingIntent时传入的是指向MainActivity的启动意图,并非广播意图,系统不会将其分发给广播接收器
- Activity内部定义的匿名广播接收器未做动态注册,系统无法感知其存在
- 独立广播接收器的
onReceive方法默认会传入Context参数,可直接用该参数获取系统服务、启动Activity,不存在无Context可用的问题
推荐实现方式(独立广播接收器,适配后台触发场景)
这种方式不需要依赖Activity存活,就算APP退到后台也能正常接收闹钟触发事件,是定时类需求的首选方案。
步骤1:创建独立的广播接收器类
新建AlarmReceiver.kt文件,代码如下:
class AlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { context ?: return // 播放指定音频 val mp = MediaPlayer.create(context, R.raw.song_title) mp.setVolume(1.0f, 1.0f) mp.start() // 直接用onReceive传入的context更新下一次闹钟 val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val nextIntent = Intent(context, AlarmReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast( context, 0, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 示例:设置次日8点的下次闹钟 val nextTriggerTime = Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, 1) set(Calendar.HOUR_OF_DAY, 8) set(Calendar.MINUTE, 0) set(Calendar.SECOND, 0) set(Calendar.MILLISECOND, 0) }.timeInMillis alarmManager.setAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent ) } }
步骤2:在AndroidManifest.xml中注册接收器
在application标签内添加如下配置:
<receiver android:name=".AlarmReceiver" android:exported="false" />
步骤3:在MainActivity中设置闹钟
修正后的onCreate代码:
private lateinit var alarmManager: AlarmManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager // 创建指向AlarmReceiver的广播意图 val intent = Intent(this, AlarmReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast( this, 0, intent, // Android12+必须指定PendingIntent可变性,普通定时场景用IMMUTABLE即可 PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 设置1分钟后触发 val triggerTime = Calendar.getInstance().apply { add(Calendar.MINUTE, 1) set(Calendar.SECOND, 0) set(Calendar.MILLISECOND, 0) }.timeInMillis alarmManager.setAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent ) }
可选实现方式(Activity内部动态注册,仅前台生效)
如果你的定时需求仅在APP前台运行时生效,可以选择将广播接收器写在Activity内部,注意必须完成动态注册和注销。
完整代码示例:
class MainActivity : AppCompatActivity() { private lateinit var alarmManager: AlarmManager // 自定义广播Action,用于匹配接收器 private val ALARM_ACTION = "com.your_package_name.ALARM_TRIGGER" // 广播接收器作为类成员变量 private val broadCastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == ALARM_ACTION) { val mp = MediaPlayer.create(context, R.raw.song_title) mp.setVolume(1.0f, 1.0f) mp.start() // 可直接访问Activity内的alarmManager更新下次闹钟 } } } override fun onResume() { super.onResume() // 动态注册广播接收器 registerReceiver(broadCastReceiver, IntentFilter(ALARM_ACTION)) } override fun onPause() { super.onPause() // 注销接收器,避免内存泄漏 unregisterReceiver(broadCastReceiver) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager // 构建带自定义Action的广播意图 val intent = Intent(ALARM_ACTION).apply { // 适配Android8.0+隐式广播限制,指定当前APP包名 setPackage(packageName) } val pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) val triggerTime = Calendar.getInstance().apply { add(Calendar.MINUTE, 1) set(Calendar.SECOND, 0) set(Calendar.MILLISECOND, 0) }.timeInMillis alarmManager.setAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent ) } }
注意事项
- 若有多个不同的定时任务,创建PendingIntent时要使用不同的requestCode参数,否则会互相覆盖
- Android 6.0+的低电耗模式下,
setAndAllowWhileIdle方法每15分钟最多触发一次,若需要更高精度的定时,可申请忽略电池优化权限 - 动态注册的广播接收器必须对应注销,否则会引发内存泄漏
内容的提问来源于stack exchange,提问作者nayriz
相关产品推荐
相关产品推荐

