Service 基础
本教程共 100 篇 · 第 32 篇 · 更新于 2026-07-28 · 约 9 分钟阅读
32. Service 基础
本节目标:理解 Service 是什么、两种服务(启动式与绑定式)的区别、Service 生命周期、为什么 Service 默认跑主线程,以及现代项目该不该用 Service。
Service 是什么
服务(Service)是没有界面的组件,用于在后台执行长时间运行的操作。即使用户切到别的应用,Service 也可能继续运行。
典型场景:
- 后台播放音乐。
- 后台下载/上传文件。
- 跟服务器保持长连接。
- 执行定时同步。
Warning
- Service 默认跑在主线程。它不会自己开线程。在 Service 里做耗时操作(网络、数据库、文件 IO)一样会 ANR。必须在 Service 内部开线程(协程、线程池)。
Service vs Thread
很多人搞不清什么时候用 Service 什么时候用 Thread:
| Service | Thread / 协程 | |
|---|---|---|
| 生命周期 | 独立于 Activity,Activity 销毁还能跑 | 跟创建它的组件绑定,Activity 销毁一般也停 |
| 用户离开应用 | 可继续运行(受后台限制) | 一般停止 |
| 是否有 Context | 是,能启动组件、访问资源 | 不是 |
| 何时用 | 需要后台长期运行、跨组件 | 单纯把耗时操作挪出主线程 |
简单说:只想在 Activity 内部异步做事,用协程就够了。需要用户离开应用后还继续运行,才考虑 Service。
现代 Android 大量后台场景用 WorkManager 替代 Service(第 75 章讲),但音乐播放、定位导航等需要持续可见的场景仍用前台 Service。
两种 Service
1. 启动式服务(Started Service)
其他组件通过 startService() 启动,Service 在后台独立运行,跟启动者无关。启动者销毁了 Service 还在跑。Service 自己干完活调 stopSelf() 退出,或别人调 stopService() 停它。
适用:一次性后台任务,如下载文件、上传日志。
2. 绑定式服务(Bound Service)
其他组件通过 bindService() 绑定,建立客户端-服务端关系。绑定者能调用 Service 的方法(通过 IBinder)。所有绑定者都解绑后,Service 销毁。
适用:需要跟 Service 交互的场景,如音乐播放器 UI 控制 Service 播放/暂停。
一个 Service 能同时是启动式和绑定式,实现 onStartCommand 和 onBind 两个回调即可。
声明 Service
在 Manifest 注册:
<application ...>
<service
android:name=".DownloadService"
android:exported="false" />
</application>
android:name:Service 类名,必须。android:exported:是否能被其他应用启动。false表示只限本应用。android:foregroundServiceType:前台服务类型(API 34+ 强制)。
Note
- 启动 Service 必须用显式 Intent。隐式 Intent 启动 Service 从 Android 5.0 起抛异常(安全考虑)。
启动式 Service 示例
class DownloadService : Service() {
override fun onCreate() {
super.onCreate()
// Service 创建时调用(只一次)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val url = intent?.getStringExtra("url") ?: return START_NOT_STICKY
// 在协程里执行耗时操作
serviceScope.launch(Dispatchers.IO) {
try {
downloadFile(url)
// 完成后停止自己
stopSelf(startId)
} catch (e: Exception) {
Log.e("DownloadService", "下载失败", e)
stopSelf(startId)
}
}
return START_NOT_STICKY
}
override fun onDestroy() {
super.onDestroy()
serviceScope.cancel()
}
override fun onBind(intent: Intent?): IBinder? = null // 启动式返回 null
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
}
启动:
val intent = Intent(context, DownloadService::class.java).apply {
putExtra("url", "https://example.com/file.zip")
}
ContextCompat.startForegroundService(context, intent) // Android 8+ 用这个
// 或 context.startService(intent)(API 26+ 后台启动受限)
onStartCommand 返回值
返回值决定 Service 被系统杀后怎么重启:
| 返回值 | 含义 |
|---|---|
START_STICKY | 重启 Service,但 intent 为 null。适合不依赖 intent 的长期服务(如音乐) |
START_NOT_STICKY | 不重启。适合一次性任务(如下载) |
START_REDELIVER_INTENT | 重启并重传最后一个 intent。适合必须完成的任务 |
绑定式 Service 示例
class MusicService : Service() {
private val binder = LocalBinder()
private var mediaPlayer: MediaPlayer? = null
inner class LocalBinder : Binder() {
fun getService(): MusicService = this@MusicService
}
override fun onCreate() {
super.onCreate()
mediaPlayer = MediaPlayer()
}
override fun onBind(intent: Intent?): IBinder = binder
fun play(path: String) {
mediaPlayer?.apply {
reset()
setDataSource(path)
prepare()
start()
}
}
fun pause() = mediaPlayer?.pause()
fun stop() = mediaPlayer?.stop()
override fun onDestroy() {
mediaPlayer?.release()
mediaPlayer = null
super.onDestroy()
}
}
绑定:
class MusicActivity : ComponentActivity() {
private var musicService: MusicService? = null
private var bound = false
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as MusicService.LocalBinder
musicService = binder.getService()
bound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
musicService = null
bound = false
}
}
override fun onStart() {
super.onStart()
Intent(this, MusicService::class.java).also { intent ->
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
if (bound) {
unbindService(connection)
bound = false
}
}
fun onPlayClick() = musicService?.play("/sdcard/song.mp3")
}
Service 生命周期
启动式:
onCreate -> onStartCommand -> [运行] -> onDestroy
绑定式:
onCreate -> onBind -> [绑定中] -> onUnbind -> onDestroy
混合:
onCreate -> onStartCommand -> onBind -> [运行] -> onUnbind -> onDestroy
onCreate:创建时调一次,做初始化。onStartCommand:每次startService都调(启动式)。onBind:bindService时调,返回 IBinder(绑定式)。onUnbind:所有客户端解绑时调。onDestroy:Service 销毁时调,释放资源。
后台限制(重要)
Android 8.0(API 26)起对后台 Service 严格限制:
- 应用在后台时,不能启动后台 Service。
startService会抛IllegalStateException。 - 需要后台执行要用
startForegroundService(),并在 5 秒内调startForeground()显示通知,变成前台服务。 - 否则系统会认为违规,杀掉应用。
Android 14(API 34)进一步要求前台服务声明类型(foregroundServiceType),且类型要跟实际用途匹配,否则崩溃。第 33 章细讲。
现代替代方案
很多传统 Service 场景现在有更好的方案:
| 传统做法 | 现代推荐 |
|---|---|
| 后台上传/下载 | WorkManager(第 75 章) |
| 定时同步 | WorkManager 周期任务 |
| 后台播放音乐 | 前台 Service + MediaSession(仍用 Service) |
| 后台定位 | 前台 Service(location 类型) |
| Activity 内异步 | 协程(不用 Service) |
Tip
- 新项目别滥用 Service。能用 WorkManager 解决的后台任务就用 WorkManager。只有需要持续可见的后台操作(音乐、导航、录屏)才用前台 Service。IntentService 已废弃,别用。
已废弃的类
- IntentService:Android 11 起废弃。它用工作线程依次处理 Intent,但后台限制下无法正常运行。替代:WorkManager 或自己用 Service + 协程。
- AsyncTask:API 30 起废弃。替代:协程。
小结
Service 是无界面后台组件,默认跑主线程,耗时操作要自己开线程。分启动式(startService,独立运行)和绑定式(bindService,可交互)。生命周期有 onCreate/onStartCommand/onBind/onUnbind/onDestroy。Android 8+ 后台启动 Service 受限,要用前台服务。现代项目大量场景用 WorkManager 替代 Service,只有持续可见的后台操作才用前台 Service。下一节深入前台服务。