死亡通知与对象生命周期
死亡通知与对象生命周期
上一章我们知道了服务端由线程池执行。这一章回答:服务端进程崩溃或退出后,客户端手里的 Binder 引用会怎样?怎么感知、怎么恢复?
远端进程退出后,客户端保存的 Proxy 不会自动指向重启后的服务实例。旧引用对应的 Binder 节点已经失效:后续调用可能抛出 DeadObjectException,已注册的 DeathRecipient 则会收到 binderDied() 回调。
死亡通知只报告失效,不负责恢复
| 常见误解 | 正确理解 |
|---|---|
| 远端进程退出后,本地引用会自动失效并重连 | 引用不会自动复活;驱动通知死亡后,客户端必须自己处理重连 |
linkToDeath() 注册后立刻知道进程死活 | 只有注册之后发生的死亡才会通知;注册前就死掉的引用只能通过调用失败发现 |
DeadObjectException 只来自“对方抛异常” | 它通常表示远端对象已死或事务已无法送达,是引用失效的信号 |
linkToDeath() 建立的是死亡观察关系,不是重连策略。回调通常运行在 Binder 线程,客户端 需要自行清理旧状态、避免并发重复恢复,并重新查询服务、注册回调和恢复必要状态。
核心结论
- 服务端进程退出时,驱动清理它持有的 Binder 节点,并通知所有注册了死亡监听的客户端。
- 客户端在
binderDied()回调里清理缓存、决定是否重新getService()。 - 未注册监听的客户端,在后续调用时会收到
DeadObjectException。 unlinkToDeath()可以取消监听;注册与取消必须成对,避免回调泄漏。- 服务重启后句柄可能变化,旧引用不可复用,必须重新获取。
死亡通知主链
逐步解释:
- 注册:客户端拿到引用后调用
linkToDeath(),驱动记录死亡监听者。 - 进程退出:服务端进程结束,驱动执行清理,释放其 Binder 节点。
- 通知:驱动向持有该节点引用的客户端发送死亡通知,客户端 Binder 线程触发
binderDied()。 - 恢复:客户端在回调里清理缓存、重新查询服务。
- 无监听兜底:没注册监听的引用,下次调用直接抛
DeadObjectException。
为什么不能自动重连
服务名没变,但服务进程重启后是全新的 Binder 节点,旧句柄指向的节点已经失效。系统无法 替客户端判断“要不要重连、重连哪个名字、重连后缓存怎么换”,所以重连必须由业务侧完成。 重连策略通常是:binderDied() 里置空缓存 → 延迟重试 getService() → 成功后再恢复调用。
源码证据
阅读目标:确认注册与回调的入口。
正文指针:BinderProxy.java linkToDeath()/unlinkToDeath();IBinder.DeathRecipient; Binder.java 中 Native 层的死亡监听接入。
// BinderProxy.java(伪代码,行号以 r75 为准)
public void linkToDeath(DeathRecipient recipient, int flags) { ... }
public boolean unlinkToDeath(DeathRecipient recipient, int flags) { ... }
// IBinder.DeathRecipient(伪代码)
public interface DeathRecipient {
void binderDied();
}这段代码证明: 注册与取消是成对 API;binderDied() 是客户端必须自己实现的回调。 驱动负责通知“节点失效”,但“之后怎么办”完全由回调里的业务代码决定。
验证与排障
环境:任意开发设备即可;复现场景需要能重启服务或杀掉进程。
adb shell am force-stop <服务包名>
adb logcat -v threadtime | grep -E "binderDied|DeadObject|linkToDeath"复现步骤:先注册监听,再 force-stop 服务进程,观察是否收到 binderDied;未注册监听时, 在服务重启后调用接口,观察 DeadObjectException。如果回调没触发,优先检查是否在错误 线程注册、是否用了已失效的引用。
常见误区
- “注册了死亡通知引用就不会死”:通知只是告知,不改变引用生命周期。
- “
binderDied()运行在主线程”:它由 Binder 线程触发,需要切线程再更新 UI 或缓存。 - “服务重启后旧引用还能用”:必须重新查询,句柄已经变化。
- “
linkToDeath可以不unlinkToDeath”:成对使用,否则可能持有无用监听导致泄漏。
延伸问题
linkToDeath的 flags 参数在什么场景会用到?- 驱动如何区分“进程主动退出”和“被系统杀死”,对通知有什么影响?
- 系统服务常用的
ServiceManager.addServiceListener()与死亡通知是什么关系? - 重试策略如何避免“服务还没起来就无限重试”?
源码入口
| 文件 | 关键位置 | 作用 |
|---|---|---|
BinderProxy.java | linkToDeath() / unlinkToDeath() | 注册与取消死亡监听 |
IBinder.java | DeathRecipient | 回调接口定义 |
Binder.java | Native 死亡监听接入 | Java 对象与驱动通知桥接 |
BinderInternal.java | 死亡通知相关 | 系统侧生命周期管理 |
公共路径:Java 侧 frameworks/base/core/java/android/os/ 与 frameworks/base/core/java/com/android/internal/os/。行号以 r75 检索为准。
需要把这些机制用于故障定位时,见 Binder 排障。