Android Kotlin定时通知服务实现方案及代码示例请求
Android 精准定时通知实现方案(Kotlin)
普通后台Service在App进程被杀死后会停止工作,要实现App关闭/关机重启后仍能触发定时通知,需要结合AlarmManager(系统级定时触发)、BroadcastReceiver(接收触发事件)和开机广播接收器(重启后恢复定时),以下是完整实现代码:
1. AndroidManifest.xml 配置
添加必要权限并注册组件:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 可选,防止触发时设备休眠 --> <application ...> <!-- 开机广播接收器 --> <receiver android:name=".BootCompletedReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> <!-- 适配部分机型 --> </intent-filter> </receiver> <!-- 定时通知广播接收器 --> <receiver android:name=".NotificationReceiver" /> <!-- 前台Service(若需后台执行额外任务) --> <service android:name=".NotificationForegroundService" android:foregroundServiceType="dataSync" /> </application>
2. 开机广播接收器(BootCompletedReceiver.kt)
手机开机后重新设置所有定时任务:
class BootCompletedReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (Intent.ACTION_BOOT_COMPLETED == intent.action || "android.intent.action.QUICKBOOT_POWERON" == intent.action) { // 重新设置所有需要的定时通知 AlarmScheduler.scheduleAlarm(context, 15, 0) // 15:00 AlarmScheduler.scheduleAlarm(context, 17, 30) // 17:30 } } }
3. 定时通知广播接收器(NotificationReceiver.kt)
接收AlarmManager触发的事件,发送通知(或启动前台Service):
class NotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // 发送通知 showNotification(context) // 如果需要执行耗时操作,启动前台Service // val serviceIntent = Intent(context, NotificationForegroundService::class.java) // ContextCompat.startForegroundService(context, serviceIntent) } private fun showNotification(context: Context) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Android 8.0+ 通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "TIMER_NOTIFICATION_CHANNEL", "定时通知", NotificationManager.IMPORTANCE_DEFAULT ).apply { description = "用于接收定时触发的通知" } notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(context, "TIMER_NOTIFICATION_CHANNEL") .setSmallIcon(R.drawable.ic_notification) // 替换为你的通知图标 .setContentTitle("定时通知") .setContentText("到点啦!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true) .build() notificationManager.notify(System.currentTimeMillis().toInt(), notification) } }
4. 前台Service(可选,NotificationForegroundService.kt)
若需要在后台执行耗时任务,使用前台Service避免被系统杀死:
class NotificationForegroundService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // 创建前台通知 val notification = NotificationCompat.Builder(this, "TIMER_NOTIFICATION_CHANNEL") .setSmallIcon(R.drawable.ic_notification) .setContentTitle("后台任务执行中") .setContentText("正在处理定时任务") .setPriority(NotificationCompat.PRIORITY_LOW) .build() startForeground(1, notification) // 执行你的耗时任务,比如数据同步等 // doWork() // 任务完成后停止前台服务 stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() return START_NOT_STICKY } override fun onBind(intent: Intent): IBinder? { return null } }
5. 闹钟调度工具类(AlarmScheduler.kt)
封装AlarmManager的定时逻辑,适配不同Android版本:
object AlarmScheduler { fun scheduleAlarm(context: Context, hour: Int, minute: Int) { val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val intent = Intent(context, NotificationReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast( context, getUniqueRequestCode(hour, minute), // 每个定时任务用唯一的请求码 intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 设置触发时间(当天的指定时分) val calendar = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, hour) set(Calendar.MINUTE, minute) set(Calendar.SECOND, 0) set(Calendar.MILLISECOND, 0) // 如果当前时间已经过了指定时间,设置为第二天的同一时间 if (timeInMillis < System.currentTimeMillis()) { add(Calendar.DAY_OF_MONTH, 1) } } // 适配Android版本,使用精准触发的API when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent ) } Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent ) } else -> { alarmManager.setExact( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent ) } } } // 生成唯一的请求码,避免不同定时任务的PendingIntent冲突 private fun getUniqueRequestCode(hour: Int, minute: Int): Int { return hour * 100 + minute } }
6. 启动方式
在App首次启动时(比如MainActivity的onCreate方法)调用闹钟设置:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 请求通知权限(Android 13+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1001) } } // 设置定时通知 AlarmScheduler.scheduleAlarm(this, 15, 0) AlarmScheduler.scheduleAlarm(this, 17, 30) } }
注意事项
- Doze模式适配:使用
setExactAndAllowWhileIdle可以让闹钟在Doze模式下也能触发,但频繁触发可能会被系统限制,尽量避免短间隔的定时任务。 - 电池优化:部分厂商的电池优化会影响AlarmManager的触发,建议引导用户将App加入电池白名单。
- 请求码唯一性:每个定时任务的PendingIntent请求码必须唯一,否则会覆盖之前的任务。
内容的提问来源于stack exchange,提问作者nope
相关产品推荐
相关产品推荐

