1. 前台服务概述
说实话,做 Android 开发这么久,每次遇到前台服务,我心里都会多留个心眼。它太容易被滥用了,又太容易被忽略细节。很多人觉得——不就是加个通知嘛?其实远不止这么简单。
这一章,我们先打好地基。把前台服务是什么、怎么启动、怎么销毁,彻底搞明白。后面聊 CPU 抖动的问题,你才会有感觉。
什么是前台服务?
前台服务,说白了,就是一种用户能“感知到”的后台任务。系统会用一条通知栏消息,明明白白告诉用户:你的 App 正在干某件事。
嗯,这里要注意:
- 它本质上还是
Service,四大组件之一。 - 但它比普通 Service 更“不容易被系统杀死”。
- 它强制要求一个可见的通知,不能偷偷摸摸跑。
我当初刚入行时,就踩过一次坑。当时做了一个音乐播放器,觉得后台播放没必要加通知。结果 Android 8.0 一上,播放器跑几分钟就被系统干掉了。用户骂翻了评论区。从那以后,我对前台服务的“通知可见性”再也不敢马虎。
启动方式
启动前台服务,其实就三步。但三步里每一步都有坑。
第一步:在 AndroidManifest 里声明
<service
android:name=".MyForegroundService"
android:foregroundServiceType="mediaPlayback"
android:exported="false" />
你想想看,很多人会忘记 foregroundServiceType。Android 10 之后,这个字段是强制的。不写,系统会给你弹一个 ForegroundServiceTypeNotAllowedException。
第二步:启动服务
val intent = Intent(this, MyForegroundService::class.java)
ContextCompat.startForegroundService(this, intent)
startService() 已经不能直接启动前台服务了。必须用 startForegroundService()。否则系统直接当成后台 Service 处理。
第三步:在 Service 内调用 startForeground()
class MyForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
// 关键:5 秒内必须调用,否则系统会 ANR 并杀死服务
startForeground(NOTIFICATION_ID, notification)
return START_STICKY
}
}
这里有一个隐藏陷阱。我曾经就遇到过一个同事,在 onCreate() 里调用了 startForeground(),结果服务还没完全初始化,通知构建失败了。5 秒窗口期一过,系统直接抛了一个 Context.startForegroundService() did not then call Service.startForeground() 的异常。很尴尬。
我个人习惯的做法是:在 onStartCommand() 里先做好所有初始化,最后调用 startForeground(),确保万无一失。
生命周期
前台服务的生命周期,比普通 Service 多了一个“通知绑定”的环节。整个流程是这样的:
| 回调 | 说明 | 常见坑点 |
|---|---|---|
onCreate() |
服务创建,初始化资源 | 不要在这里调用 startForeground() |
onStartCommand() |
接收启动请求,调用 startForeground() | 5 秒窗口期,超时不调用就 ANR |
| 服务运行中 | 持续执行任务,通知可见 | 留意 CPU 占用,会引发 Compose 重组抖动 |
onDestroy() |
停止前台,移除通知,释放资源 | 记得调用 stopForeground() 和 stopSelf() |
关于 onStartCommand() 的返回值,我建议你用 START_STICKY。为什么呢?
START_STICKY:系统杀死服务后会重新创建,但会传一个 null Intent。适合需要持续运行的任务。START_NOT_STICKY:系统杀死后不再重启。适合一次性任务。START_REDELIVER_INTENT:会重新传递上次的 Intent。适合需要完整恢复的任务。
我个人习惯是:如果你不确定,用 START_STICKY 是最稳妥的。但要注意——重新创建时的 null Intent 处理,很多人会忽略。
最后,做一张图把前台服务的启动流程和生命周期的核心脉络串起来,方便你记忆。
流程图看下来,你会发现核心就两个:通知绑定和生命周期回调。但就是这两个看似简单的东西,在 CPU 占用高的时候,会给 Compose 的重组机制带来肉眼可见的卡顿。下一章,我们会深入分析这个问题。
接下来,就是实战的部分了。