在 AIDL 调用中,如何处理客户端和服务端的异常断开?

解读

国内面试场景下,这道题考察的是“Binder 死亡通知”机制与工程化落地能力。面试官想确认:

  1. 你是否知道 Binder 链路异常(进程被杀、断网、系统回收)的本质是 Binder Driver 的 BR_DEAD_BINDER 事件;
  2. 能否把系统回调(IBinder.DeathRecipient)与业务层解耦,做到可测试、可灰度;
  3. 是否熟悉国产 ROM 的“强杀”特性(如 MIUI 的“强制停止”与华为“应用启动管理”)对 Binder 存活的影响;
  4. 能否给出线上监控与自动重连的完整方案,而不是只背 API。

知识点

  1. Binder 死亡通知流程:内核感知→发送 BR_DEAD_BINDER→BinderProxy 回调 DeathRecipient→移除引用。
  2. IBinder.linkToDeath/unlinkToDeath 必须在远端接口返回后、且在主线程外注册,防止 Race。
  3. ServiceConnection.onServiceDisconnected 只会在“被系统解绑”时回调,国产 ROM 强杀进程不会触发,必须配合 DeathRecipient。
  4. 重连策略:指数退避 + 最大次数 + 应用前后台状态感知;前台 0/1/2/3 s,后台 5/10/30 s,避免后台拉起被系统拦截。
  5. 异常分类与埋点:Binder 内部异常(DEAD_OBJECT、TRANSACTION_FAILED)、业务异常(RemoteException)、进程死亡(AMS 日志 tag=ActivityManager,reason=killed)。
  6. 国内厂商特殊限制:华为“关联启动”、OPPO“自启动管理”、vivo“后台高耗电”白名单,需在初始化时引导用户加锁。
  7. 灰度验证:使用 adb shell am force-stop 模拟强杀;使用 monkey 压力测试 30 min,验证重连成功率 ≥ 99.5%。

答案

分四层回答:注册、回调、重连、监控。

  1. 注册死亡通知
    在 ServiceConnection.onServiceConnected 里拿到 IBinder 后立即注册:

    try {
        binder.linkToDeath(new IBinder.DeathRecipient() {
            @Override public void binderDied() {
                binder.unlinkToDeath(this, 0);   // 必须解绑,防止内存泄漏
                onBinderDied();                  // 进入重连流程
            }
        }, 0);
    } catch (RemoteException e) {
        // 远端已死,直接重连
        onBinderDied();
    }
    

    注意:linkToDeath 不能在主线程做,否则 ANR;推荐在单线程后台 Handler 执行。

  2. 统一回调入口
    定义一个 Lifecycle-aware 的 BinderPool:

    class AidlConnector(private val app: Application) {
        private val handler = Handler(Looper.getMainLooper())
        private var binder: IMyAidlInterface? = null
        private val deathRecipient = object : IBinder.DeathRecipient {
            override fun binderDied() {
                binder?.unlinkToDeath(this, 0)
                binder = null
                handler.post { retryBind() }   // 切回主线程重连
            }
        }
        fun bind() {
            val intent = Intent(app, RemoteService::class.java)
            intent.setPackage(app.packageName)
            app.bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
        private val connection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName, service: IBinder) {
                try {
                    service.linkToDeath(deathRecipient, 0)
                    binder = IMyAidlInterface.Stub.asInterface(service)
                } catch (e: RemoteException) {
                    retryBind()
                }
            }
            override fun onServiceDisconnected(name: ComponentName) {
                // 系统解绑,仍需重连
                binder = null
                retryBind()
            }
        }
        private fun retryBind() {
            val delay = RetryPolicy.nextDelay()
            handler.postDelayed({ bind() }, delay)
        }
    }
    
  3. 重连策略
    采用指数退避 + 上限 5 次,前台与后台区分:

    object RetryPolicy {
        private val max = 5
        private var count = 0
        fun nextDelay(): Long {
            if (count >= max) return -1   // 放弃,落盘上报警
            val base = if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) 1000 else 10000
            return (base shl count++).coerceAtMost(60000)
        }
        fun reset() { count = 0 }
    }
    

    当重连成功时,在 onServiceConnected 里调用 RetryPolicy.reset()。

  4. 线上监控

    • 埋点:Binder 死亡事件上传日志字段(reason=binder_died, retry_count, foreground)。
    • 指标:日活用户 Binder 断开率 < 0.3%,重连成功率 > 99%。
    • 兜底:若远端为系统级服务(如厂商 SDK),在检测到 5 次失败后弹窗引导用户加白名单;若为自身多进程服务,则触发 JobScheduler 拉起。

拓展思考

  1. 如果服务端是另一个 APK(例如厂商支付插件),而对方没有加白名单,重连仍会被系统拦截。此时可改用“前台服务 + startForeground”提高优先级,或协商对方提供 ContentProvider 做轻量级心跳,降级为 CP 调用。
  2. 在车载场景(Android Automotive)中,系统更新或车机休眠会杀掉所有非系统 Binder。可以利用 CarService 提供的 Car 连接状态广播,在收到 ACTION_CAR_SERVICE_CONNECTED 后再重新 bind,避免无效重试。
  3. 对于高并发 RPC(如广告 SDK),可在客户端做“无感知切换”:维护两个 Binder 连接(主 + 备),DeathRecipient 触发后把流量切到备链路,同时后台再拉一条新链路,实现 0 丢帧。
  4. 未来 Binder 恢复方案:Android 14 引入“Binder IPC 自省”与“链路冻结”机制,可监听 mAlive 属性。面试时可提及“我正在关注 AOSP 14 的 BinderRef 暴露接口,评估在系统层做链路自愈的可行性”,体现技术前瞻性。