Android 中 Service 用法
这篇就讲一件事:Service 到底什么时候用,怎么用,怎么不踩坑。
你可以先把 Service 理解成:
"App 没有页面展示时,仍然需要在后台继续干活的一个组件"。
就像关注博主,下次容易找到一样。
点点关注一下
常见场景:
- 音乐后台播放
- 文件上传下载
- 定位持续上报
- 蓝牙设备连接与通信
1. 先记住这 3 句话
Service不是线程,它默认也跑在主线程。- 耗时任务不能直接写在
Service里,不然容易 ANR。 - 真正长期运行的后台任务,通常要做成前台服务(带通知)。
2. Service 有两种常用玩法
简单说:
- 启动式:我让你去干活,你自己干完自己停。
- 绑定式:我需要一直和你通信,比如"播放/暂停/下一首"。
2.1 启动式 Service(Started Service)
启动方式:
startService(intent)(老方式)- Android 8+ 更常见
startForegroundService(intent)(配合前台服务)
适合场景:
- 同步数据
- 上传日志
- 一次性后台处理
核心回调:
onCreate():第一次创建时调用一次onStartCommand():每次启动都会进来onDestroy():销毁时调用
停止方式:
- 在服务里
stopSelf() - 外部
stopService(intent)
2.2 绑定式 Service(Bound Service)
绑定方式:bindService(intent, conn, flags)
适合场景:
- 音乐播放器控制
- 下载进度回调
- 页面和服务需要频繁交互
核心回调:
onCreate()onBind()(返回IBinder)onUnbind()/onRebind()onDestroy()
一句话理解:
页面拿到 IBinder 后,就能像调用普通对象一样调用服务方法。
3. 生命周期怎么理解更简单
启动式
onCreate() -> onStartCommand()(可能多次)-> onDestroy()
onStartCommand() 返回值怎么选:
START_NOT_STICKY:被系统杀了就算了,不自动重建(一次性任务常用)START_STICKY:系统尽量拉起来,但Intent可能没了START_REDELIVER_INTENT:系统重建时,把上次Intent再给你一次
绑定式
onCreate() -> onBind() -> 交互 -> onUnbind() -> onDestroy()
4. 什么是前台服务(Foreground Service)
你可以把前台服务理解成:
"系统允许你长时间后台运行,但你必须给用户一个持续通知,告诉用户你在做什么。"
从 Android 8 开始,后台限制变严格,很多长期任务都要走前台服务。
基本要求:
- 用
startForegroundService(intent)启动 - 很快调用
startForeground(notificationId, notification) - 通知必须可见(不能偷偷跑)
Android 14+ 额外注意:
Manifest里写foregroundServiceType- 类型要和实际任务对应(如
mediaPlayback、location、dataSync) - 相关权限要补齐(通知、定位等)
5. 代码示例(可直接参考)
5.1 启动式 Service(Kotlin)
kotlin
class SyncService : Service() {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
override fun onCreate() {
super.onCreate()
// 初始化资源
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
scope.launch {
try {
// 执行耗时任务
doSync()
} finally {
// 任务结束后停止服务
stopSelf(startId)
}
}
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
private suspend fun doSync() {
// TODO: 执行同步逻辑
}
}
启动和停止:
kotlin
ContextCompat.startForegroundService(context, Intent(context, SyncService::class.java))
// 或 context.startService(intent)(低版本或非前台需求)
context.stopService(Intent(context, SyncService::class.java))
5.2 绑定式 Service(Kotlin)
kotlin
class MusicService : Service() {
private val binder = LocalBinder()
inner class LocalBinder : Binder() {
fun getService(): MusicService = this@MusicService
}
override fun onBind(intent: Intent): IBinder = binder
fun play() {
// 播放逻辑
}
fun pause() {
// 暂停逻辑
}
}
Activity 绑定:
kotlin
class MainActivity : AppCompatActivity() {
private var musicService: MusicService? = null
private var bound = false
private val conn = 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?) {
bound = false
musicService = null
}
}
override fun onStart() {
super.onStart()
bindService(
Intent(this, MusicService::class.java),
conn,
Context.BIND_AUTO_CREATE
)
}
override fun onStop() {
super.onStop()
if (bound) {
unbindService(conn)
bound = false
}
}
}
6. Manifest 配置
xml
<service
android:name=".service.MusicService"
android:exported="false"
android:foregroundServiceType="mediaPlayback" />
说明:
android:exported="false":只给自己 App 用,默认更安全- 只有真的要给其他 App 调用,才考虑
exported="true",并加权限保护
7. 到底该不该用 Service
这些情况适合用 Service:
- 用户能感知到、而且要持续运行的任务(音乐、导航、运动)
- App 切后台后,任务还要继续
这些情况不建议直接用 Service:
- 一次性、可延迟、要求"最终一定执行"的任务 -> 优先
WorkManager - 页面内短耗时任务 -> 协程 + 生命周期组件就够了
8. 常见问题与最佳实践
-
别在主线程做重活
Service默认是主线程,耗时操作请放协程/线程池。 -
干完就停
任务完成后及时
stopSelf(),别让服务白跑。 -
前台服务别滥用
有持续通知会打扰用户,必须有明确业务价值。
-
注意系统版本差异
Android 8+ 后台限制、Android 13+ 通知权限、Android 14+ 前台服务类型都要适配。
-
考虑被系统杀进程
按业务选好
onStartCommand()返回值,并做好状态恢复。 -
跨进程通信是进阶题
真要给其他 App 调用,通常走 AIDL + 权限控制。
9. 一句话总结
Service 适合"没页面但要持续干活"的任务:
只管执行用启动式 ,需要交互控制用绑定式 ,长期后台任务优先做前台服务并按系统版本补齐权限和类型声明。