一句话总结:
服务(Service)启动就像雇人干活——要么长期包养(startService),要么临时雇佣随叫随到(bindService),通信全靠“传纸条”(Binder、广播等)!
一、服务的两种“契约”:启动与绑定
将 startService 和 bindService 理解为与服务签订的两种不同“契约”。
| 契约类型 | startService() | bindService() |
|---|---|---|
| 契约模型 | 启动-停止模型(Fire and Forget) | 客户端-服务器模型(Client-Server) |
| 生命周期 | 与调用者无关。一旦启动,除非主动调用stopSelf()/stopService()或系统回收,否则会一直运行。 | 与调用者绑定。所有客户端解绑(unbind)后,服务便会销毁(除非它同时也被start了)。 |
| 通信方式 | 单向。调用者通过Intent传递初始数据,但无法直接获得服务返回结果或调用其方法。 | 双向。客户端通过返回的IBinder对象与服务进行方法调用、数据交换等高效通信。 |
| 核心场景 | 音乐播放、文件下载等需要独立于UI运行的长时任务。 | 需要与UI频繁交互,实时控制或获取服务内部状态的场景。 |
二、核心进阶:有状态的混合启动模式
这是理解Service的关键:startService和bindService不是互斥的,而是可以组合的。
-
场景:一个音乐播放器。
- 用户点击播放,调用
startService()启动服务。这保证了即使用户退出App,音乐依然能继续播放(契约1:长期运行)。 - 当用户回到播放界面(
Activity)时,界面需要显示播放进度并控制暂停/下一首。此时调用bindService()。这让Activity拿到了服务的Binder,可以与之直接通信(契约2:临时交互)。 - 用户离开界面时,调用
unbindService(),解除了交互绑定,但由于服务是被start的,它并不会被销毁,音乐继续播放。
- 用户点击播放,调用
结论: 一个Service的最终销毁,需要同时满足两个条件:
- 没有任何客户端与它绑定 (
isBound为false)。 - 它没有处于“已启动”状态(即从未调用
startService,或已调用stopService/stopSelf)。
三、服务的线程本质与现代后台方案
1. 致命误区:Service 运行在主线程
Service的所有生命周期回调(onCreate, onStartCommand, onBind等)默认全部在应用的主线程执行。在其中执行任何耗时操作(如网络请求、文件读写)都会直接阻塞UI,导致ANR(应用无响应)。
2. 经典解决方案:IntentService (已废弃)
IntentService是Google为简化后台任务而设计的。它内部创建了一个工作线程来处理所有Intent请求,任务完成后会自动停止。虽然已被废弃,但其“任务队列+自动停止”的设计思想值得学习。
3. 现代官方推荐:WorkManager
对于绝大多数可延迟的、需要保证执行的后台任务(如数据同步、日志上传),WorkManager是目前的首选。它能感知生命周期,支持设置执行约束(如联网时、充电时),并能选择性地使用JobScheduler或BroadcastReceiver等来保证任务执行,比Service更省电、更稳定。
4. 必须立即执行的长时任务:前台服务 (Foreground Service)
自 Android 8.0 (API 26) 起,系统严格限制后台Service的运行。如果你的应用需要在后台长时间运行(如导航、音乐播放),必须将其设为前台服务。
- 实现方式:调用
startForeground(notificationId, notification)。 - 用户感知:系统会显示一个无法被用户划掉的常驻通知,明确告知用户该应用正在后台运行。这是对用户透明和负责的体现。
四、组件间通信的结构化指南
选择通信方式应基于场景,而不是随意挑选。
场景一:在同一进程内
-
客户端-服务器模式 (推荐)
- 技术:
bindService()+Binder - 优点:最高效的IPC方式,直接进行方法调用,类型安全。
- 缺点:需要编写模板代码来处理连接和解绑。
- 技术:
-
发布-订阅模式
- 技术:
LocalBroadcastManager或EventBus等事件总线库。 - 优点:完全解耦,一对多通信非常灵活。
- 缺点:滥用会导致事件流混乱,难以追溯和维护。
- 技术:
场景二:跨进程通信 (IPC)
-
简单消息传递
- 技术:
Messenger - 优点:基于
Handler,将请求放入队列串行处理,无需处理多线程问题。 - 缺点:只能传递
Message对象,无法进行直接方法调用,是单向的。
- 技术:
-
远程过程调用 (RPC)
- 技术:
AIDL(Android Interface Definition Language) - 优点:功能强大,允许定义复杂的接口,支持多线程和同步/异步调用。
- 缺点:实现复杂,需要编写
.aidl文件,并且必须手动处理多线程同步问题,是性能开销最大的IPC方式。
- 技术: