本文主要解析 Android 中unbindService的流程及服务断开连接的机制,结合 Android 6.0 源码阐述从客户端解绑服务到系统处理连接断开的完整逻辑。以下是通俗且详尽的解读:
一、unbindService 核心流程
unbindService用于断开客户端与服务的绑定连接,清理相关资源。其核心流程涉及跨进程通信(IPC)和服务连接状态管理,主要分为以下步骤:
1. 客户端发起解绑请求
当客户端调用unbindService(mConnection)时,流程如下:
-
ContextImpl.unbindService:
通过ActivityManagerProxy(AMP)向 system_server 进程的ActivityManagerService(AMS)发送解绑请求。 -
AMP.unbindService:
通过 Binder 调用 AMS 的unbindService方法,传递客户端的ServiceConnection对应的InnerConnection代理(即IServiceConnection的 Binder 对象)。
关键逻辑:
- 解绑操作需传入绑定阶段使用的
ServiceConnection,系统通过该对象找到对应的连接记录。 - 若客户端多次绑定同一服务,
unbindService会减少连接计数,只有当计数为 0 时才会触发服务销毁(若同时没有startService调用)。
2. system_server 层处理解绑
AMS 接收到解绑请求后,通过ActiveServices管理服务连接状态:
-
AMS.unbindService:
调用ActiveServices.unbindServiceLocked,根据InnerConnection查找对应的ConnectionRecord(连接记录)。 -
ActiveServices.removeConnectionLocked:-
从服务的连接列表中移除该客户端的
ConnectionRecord。 -
若服务已无任何客户端连接(计数为 0),且未被
startService启动,则触发服务销毁流程(bringDownServiceIfNeededLocked)。 -
通过
ApplicationThreadProxy(ATP)通知服务进程执行解绑逻辑(scheduleUnbindService)。
-
核心代码链:
plaintext
AMS.unbindService() → ActiveServices.unbindServiceLocked() → removeConnectionLocked() → ATP.scheduleUnbindService()
3. 服务进程处理解绑
服务进程收到解绑请求后,执行以下操作:
-
ActivityThread.handleUnbindService:
通过 Handler 消息触发handleUnbindService,调用服务的onUnbind方法(若存在)。-
onUnbind:可选回调,用于在服务被解绑时执行清理逻辑(如释放资源)。 -
若
onUnbind返回true,当有新客户端绑定时会触发onRebind回调。
-
注意:
onUnbind仅在解绑时调用,与服务是否销毁无关。- 服务销毁需同时满足 “无绑定客户端” 和 “未被
startService启动” 两个条件。
二、服务进程死亡的处理机制
当服务所在进程崩溃或被系统杀死时,客户端会通过Binder 死亡监听感知连接断开,触发onServiceDisconnected回调,流程如下:
1. Binder 死亡监听的建立
在绑定阶段,ServiceDispatcher会为服务的IBinder设置死亡监听:
java
service.linkToDeath(info.deathMonitor, 0); // deathMonitor是ServiceDispatcher内部类
DeathMonitor实现了IBinder.DeathRecipient接口,当服务进程死亡时,binderDied方法会被调用。
2. 触发onServiceDisconnected的流程
-
DeathMonitor.binderDied:
服务进程死亡后,Binder 驱动通知客户端,触发DeathMonitor的binderDied方法。 -
ServiceDispatcher.death:
移除失效的连接记录,并通过 Handler 向客户端主线程发送消息。 -
ServiceDispatcher.doDeath:
最终调用客户端的ServiceConnection.onServiceDisconnected回调,通知服务已断开。
关键代码:
java
// 客户端收到服务死亡通知后回调
public void doDeath(ComponentName name, IBinder service) {
mConnection.onServiceDisconnected(name); // 调用用户定义的回调
}
3. 与unbindService的区别
| 场景 | unbindService | 服务进程死亡 |
|---|---|---|
| 触发方式 | 客户端主动调用 | 服务进程异常终止(如崩溃、强杀) |
| 回调方法 | 不触发onServiceDisconnected | 触发onServiceDisconnected |
| 服务是否销毁 | 可能(无绑定且未启动时) | 服务进程销毁,服务实例重建需重启 |
三、开发注意事项
1. 避免内存泄漏
- 及时解绑:在
Activity的onDestroy中调用unbindService,避免ServiceConnection持有Activity引用导致泄漏。 - 弱引用处理:若
ServiceConnection是Activity的内部类,建议使用弱引用(WeakReference)避免内存泄漏。
2. 处理服务异常断开
- 监听
onServiceDisconnected:在回调中重新绑定服务或提示用户。 - 结合
startService:若希望服务在解绑后继续运行,需同时使用startService和bindService,此时解绑不会导致服务销毁。
3. 理解回调顺序
unbindService会先触发onUnbind(若有),再清理连接记录。- 服务进程死亡时,直接触发
onServiceDisconnected,不经过onUnbind。
四、总结:流程对比与核心逻辑
1. 解绑与服务死亡流程对比
| 步骤 | unbindService流程 | 服务进程死亡流程 |
|---|---|---|
| 发起方 | 客户端(主动调用) | 系统(服务进程异常) |
| 核心操作 | 移除连接记录,可能销毁服务 | 触发 Binder 死亡监听,通知客户端 |
| 回调顺序 | onUnbind(可选) | onServiceDisconnected |
| 服务生命周期 | 可能销毁(无依赖时) | 进程销毁,服务实例需重启 |
2. 核心逻辑图示
plaintext
客户端进程 system_server进程 服务进程
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ unbindService() │─── Binder ───►│ AMS.unbindService() │ │ │
│ │ │ │ └──────────────┘
│ InnerConnection │ │ removeConnectionLocked() │
│ (Binder服务端) │ │ │
└──────────────┘ └──────────────┘ ┌──────────────┐
│ onUnbind() │
└──────────────┘
▲
│ 服务正常解绑
│
├───────────────┐
│ 服务进程死亡 │
▼ │
客户端进程 system_server进程 服务进程
┌──────────────┐ ┌──────────────┐
│ onServiceDisconnected() │ │ │
│ (Binder死亡回调) │ └──────────────┘
└──────────────┘
五、常见问题解答
-
为什么调用
unbindService后服务未销毁?- 服务可能被
startService启动,需同时调用stopService。 - 其他客户端仍保持绑定,服务连接计数未清零。
- 服务可能被
-
onServiceDisconnected何时触发?- 服务进程死亡(如崩溃、被系统杀死)。
- 服务被显式销毁(
stopService且无绑定客户端)。
-
如何在解绑时获取服务的最后状态?
-
可在
onUnbind或onServiceDisconnected中通过接口设计传递状态数据,或使用持久化存储。
-
通过理解unbindService和服务死亡的底层机制,开发者能更精准地管理服务生命周期,避免资源泄漏,并处理异常断开场景,提升应用的稳定性和用户体验。