一句话总结:
Service 是 Android 的“后台打工人”,它本身运行在主线程,需要开发者手动管理工作线程。它有两种核心模式:“独立工单”(startService)和“协作接口”(bindService),并可通过“亮牌上岗”(Foreground Service)来避免被系统清退。
一、核心前提:Service的线程模型
这是一个最重要但最容易被误解的概念:Service 默认运行在主线程。它的所有生命周期回调,如 onCreate(), onStartCommand(), onBind(),都在UI线程上执行。这意味着,任何耗时操作(网络请求、文件读写)都必须在Service内部手动创建子线程来完成,否则将直接导致应用无响应(ANR)。
// 在Service中执行耗时任务的正确姿势
class MyService : Service() {
private val scope = CoroutineScope(Dispatchers.IO)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
scope.launch {
// 在这里执行网络请求、数据库读写等耗时操作
}
return START_NOT_STICKY
}
// ...
}
二、服务的两种核心模式与生命周期
1. 启动型服务 (startService):执行独立任务
这种模式如同下发一张“工单”,Service 启动后便独立运行,直到任务完成或被显式停止。它与启动它的组件没有直接联系。
-
生命周期:
onCreate()→onStartCommand()(可多次调用) →onDestroy() -
启动与停止:通过
context.startService()启动,通过context.stopService()或 Service 内部调用stopSelf()停止。 -
核心机制 - 系统契约:
onStartCommand的返回值是关键,它定义了Service被系统意外杀死后的行为:START_STICKY:系统会尝试重建服务,但不会重新传递最后一个Intent。适用于媒体播放器等需要长期运行但不在乎命令细节的场景。START_NOT_STICKY:服务被杀死后不会自动重启。适用于执行一次性任务的场景。START_REDELIVER_INTENT:系统会重建服务,并重新传递最后一个Intent。适用于文件下载等需要确保命令被执行的场景。
2. 绑定型服务 (bindService):提供交互接口
这种模式将Service作为一个服务端,允许其他组件(客户端)绑定并与之通信。
- 生命周期:
onCreate()→onBind()→onUnbind()→onDestroy()。只要有客户端绑定,Service就会保持存活。 - 通信桥梁 (
IBinder) :onBind()方法必须返回一个IBinder接口实例。客户端通过这个IBinder与Service进行方法调用、数据交换。当涉及跨进程通信(IPC)时,通常需要使用AIDL(Android接口定义语言)来定义这个接口。 - 适用场景:需要与UI进行频繁交互的后台任务,如一个带有复杂播放逻辑的音乐播放器,Activity需要调用Service的
play(),pause(),getProgress()等方法。
三、前台服务:一种提权机制,而非独立类型
前台服务并不是一种新的Service类型,而是对启动型服务的一种“提权”操作。通过调用 startForeground(notificationId, notification),服务必须在状态栏显示一个常驻通知,以此换取极高的进程优先级,系统几乎不会杀死它。
- 强制性:从Android 8.0开始,如果应用处于后台,任何希望长期运行的
startService都必须在几秒内将自己提升为前台服务,否则系统会抛出IllegalStateException。 - 适用场景:任何用户明确知道正在运行的任务,如音乐播放、导航、文件上传下载。
四、现代后台任务的最佳实践:告别IntentService
IntentService因其自动创建工作线程和任务完成后自停止的便利性曾被广泛使用。但由于其基于startService,同样受到Android 8.0后的后台执行限制,现已完全不推荐使用。
现代Android开发应根据任务需求选择合适的工具:
WorkManager:处理可延迟、可靠的后台任务的首选。它不是Service,而是一个更强大的任务调度库,能智能地根据设备状态(如网络连接、充电状态)执行任务,并保证任务最终会被执行,即使应用退出或设备重启。它是数据同步、日志上报等场景的最佳选择。- 协程 (Coroutines) :对于与应用生命周期相关的异步任务,结合
ViewModelScope或LifecycleScope使用协程是目前最简洁、安全的方式。 - 前台服务:用于那些必须立即开始且需要持续运行,直到用户主动停止的任务。
决策指引:
- 如果任务需要确保在未来某个时间点(即使App关闭)完成,并且可以延迟执行 ->
WorkManager。 - 如果任务是用户主动发起,需要立即开始并持续运行,且用户需要感知到它的存在 -> 前台服务。
- 如果任务只是为了在UI组件存在时为其提供数据或服务 ->
bindService配合ViewModel和协程。