
一、核心概念
1.1 Service 是什么
Service 是 Android 中一个没有用户界面、运行在主线程的后台组件,专门用于处理无需用户交互的长期运行操作。其生命周期比 Activity 更加独立——即便 Activity 被销毁,Service 仍可继续执行。
关键生命周期方法:onCreate() → onStartCommand() → onBind() → onDestroy()。其中 onStartCommand 的返回值决定了系统杀死进程后 Service 的行为:START_STICKY 表示自动重启,而 START_NOT_STICKY 则不会重启。
1.2 三种 Service
Service 主要分为以下三种类型:
第一种,Started Service,通过 startService() 启动,独立于启动它的组件运行。
第二种,Bound Service,通过 bindService() 绑定,提供客户端与服务端的通信接口(Binder)。
第三种,Foreground Service,带有持续通知栏提醒的服务,优先级高,不易被系统回收——这正是本文的核心内容。
1.3 为什么需要前台服务
自 Android 8.0 起,系统对后台 Service 的运行实施了严格限制:应用切换至后台几分钟后,后台 Service 会被系统终止。前台服务通过强制显示一个持续通知(ongoing notification)告知用户“服务仍在运行”,从而获得持续运行的权限。
二、Kotlin 代码实战
2.1 前台服务核心实现
下面展示一个典型的音乐播放前台服务实现代码示例:
class MusicPlaybackService : Service() {
override fun onCreate() {
super.onCreate()
createNotificationChannel()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = buildNotification()
// 必须在 startForeground() 调用后的 5 秒内执行
startForeground(NOTIFICATION_ID, notification)
// 模拟音乐播放逻辑
Log.d("MusicService", "正在播放音乐...")
return START_STICKY // 被杀后自动重启
}
private fun createNotificationChannel() {
val channel = NotificationChannel(
CHANNEL_ID,
"音乐播放",
NotificationManager.IMPORTANCE_LOW // 设为低重要性,避免通知声音干扰
)
val manager = getSystemService(NotificationManager::class.ja va)
manager.createNotificationChannel(channel)
}
private fun buildNotification(): Notification {
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("正在播放音乐")
.setContentText("音乐持续播放中...")
.setSmallIcon(R.drawable.ic_music)
.setOngoing(true) // 设置为持续通知,用户无法滑动删除
.build()
}
override fun onBind(intent: Intent?): IBinder? = null
companion object {
const val CHANNEL_ID = "music_playback"
const val NOTIFICATION_ID = 1001
}
}
2.2 Manifest 声明
从 Android 14 开始,必须显式声明 foregroundServiceType,同时在 Manifest 中声明对应类型的权限。
2.3 启动和停止
// 启动前台服务示例
val intent = Intent(context, MusicPlaybackService::class.ja va)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
// 停止服务示例
val intent = Intent(context, MusicPlaybackService::class.ja va)
context.stopService(intent)
2.4 Bound Service 进阶
class MusicPlaybackService : Service() {
private val binder = MusicBinder()
inner class MusicBinder : Binder() {
fun getService(): MusicPlaybackService = this@MusicPlaybackService
}
override fun onBind(intent: Intent?): IBinder = binder
// 暴露给客户端调用的方法
fun play(songId: String) { /* ... */ }
fun pause() { /* ... */ }
}
Activity 中绑定:
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as MusicBinder
binder.getService().play("song_123")
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
bindService(Intent(this, MusicPlaybackService::class.ja va), serviceConnection, Context.BIND_AUTO_CREATE)
三、常见陷阱与注意事项
前台服务功能强大,但使用过程中也容易踩坑。
前台服务启动超时问题:调用 startForegroundService() 后,必须在 5 秒内调用 startForeground(),否则系统会触发 ANR 并终止进程。
避免在主线程执行耗时操作:Service 默认运行在主线程,音乐解码、网络请求等任务必须另开协程或线程,否则会直接导致 ANR。
Android 12 限制从后台启动 Service:应用处于后台时直接调用 startService() 会抛出 ForegroundServiceStartNotAllowedException。解决方案:使用 WorkManager 或 AlarmManager 替代。
及时调用 stopSelf() 释放资源:任务完成后必须主动停止 Service,否则通知栏会一直保留“吸血鬼”通知,引起用户反感且增加耗电。
targetSdk 34 必须声明 foregroundServiceType:若不声明,直接导致崩溃。
四、总结
Service 是 Android 后台任务的基石,而前台服务则是 Android 8.0 及更高版本的标配方案。请牢记三条核心规则:5 秒内调用 startForeground()、不在主线程执行耗时操作、任务结束后及时调用 stopSelf()。掌握这些要点,您的音乐播放、GPS 轨迹记录、文件下载等后台任务就能稳定可靠地运行。
