You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android USB设备重连后requestWait()/queue()通信失效问题

问题根因

核心问题是USB设备断连重连后,你持有的旧UsbDeviceConnection/UsbInterface/UsbEndpoint实例已经全部失效,但代码还在复用这些实例,外加UsbRequest的使用方式完全不符合API设计逻辑。
两种传输API表现不一样的原因非常直接:

  • 同步的bulkTransfer()每次调用时,native层会临时向系统USB服务拉取当前最新的设备句柄,部分ROM下只要设备已经完成重枚举,哪怕你传的是旧connection对象,底层也能自动绑定到新句柄,所以看起来能恢复通信。但这种隐式绑定没有官方保证,且同步调用没有持续挂起的接收缓冲区,设备返回数据的时机如果卡到调用间隙就会直接丢包,这就是你之前遇到丢包的原因。
  • 异步的UsbRequest在你第一次调用initialize()时,就和传入的connection句柄做了强绑定,native层会一直持有这个旧句柄的引用。设备断连后旧句柄直接作废,后续你再调用initialize()传旧的失效connection,queue()和requestWait()只会静默失败——要么queue返回false你没处理,要么requestWait直接阻塞/返回null,不会主动抛异常提醒你连接失效,自然收不到返回的payload。

你代码里还有几个直接加重问题的逻辑错误:

  • 把UsbRequest定义成AsyncTask的全局成员,每次收发数据都反复initialize/close。这个API本身设计是支持连接生命周期内复用的,反复销毁重建在连接状态波动时,极容易造成native层的引用泄漏,进一步导致请求无响应。
  • 没有监听USB设备拔插广播。设备断开时你没有主动释放旧连接、取消正在运行的传输任务,重连后也没有重新走权限申请、接口认领、端点获取的全流程,全靠旧对象凑合用。
  • 接收成功的判断逻辑完全无效:拿request.hashCode()当接收结果判断依据,只要Java层的request对象没被回收,这个值永远大于0,根本没法区分请求是真的收到了数据,还是直接静默失败了,重连后就算queue失败你也会误以为接收成功,拿空buffer解析数据。
  • 全程没有校验connection的有效性,设备断开后之前的claimInterface早就失效了,你还在往旧端点上挂请求。
修复步骤

按优先级改,改完就能解决:

  1. 先补全USB连接生命周期监听
    静态注册广播监听ACTION_USB_DEVICE_ATTACHED和ACTION_USB_DEVICE_DETACHED事件:
    • 收到断开事件时,立刻终止所有正在运行的传输任务,调用releaseInterface()、close()释放旧连接,把全局存的connection、interface、读写端点实例全部置空。
    • 收到重连事件时,从头走USB权限申请流程,拿到全新的UsbDeviceConnection实例,重新认领接口、获取读写端点,再启动新的传输任务。断连前生成的所有USB相关对象一个都不要复用。
  2. 修正UsbRequest的使用逻辑
    不要用全局的UsbRequest实例,每次新建传输任务、或者连接重建时,生成新的UsbRequest对象绑定到新连接上:
    • 调用requestWait()时必须判断返回值:只有返回值等于你当前提交的UsbRequest实例,且对应ByteBuffer的position大于0,才是真的收到了有效数据,绝对不要用对象hashCode判断请求结果。
    • 只要queue()返回false,立刻终止当前传输流程,触发连接重连逻辑,不要盲目重试。
  3. 补全异步传输流控解决丢包
    之前用bulkTransfer丢包本质是没有预留持续挂起的接收缓冲区,用异步API时维护好两个队列就能彻底解决:
    • 发送队列:所有待发送payload先入队,等上一个发送请求的requestWait()正常返回后,再提交下一个发送请求,不要无间隔连续堆请求。
    • 接收队列:连接建立成功后,先在读端点上提交1-2个带buffer的接收请求,等requestWait()返回收到数据、处理完业务逻辑后,立刻补一个新的接收请求到读端点,保证读端点上始终有等待接收的buffer,就不会丢数据。
  4. 删掉无意义的忙等逻辑
    那个busyWaitMicros(20)纯占CPU资源,没有任何同步作用——异步传输本身靠requestWait()的返回做同步点,不需要额外加sleep或者忙等。
验证方法

改完后打日志对比:重连后拿到的UsbDeviceConnection对象hashCode和断连前的不一致,所有UsbRequest都是基于新连接初始化的,queue()返回true,requestWait()能返回对应提交的请求对象,ByteBuffer里有设备实际写入的返回数据,通信就恢复正常了。

内容的提问来源于stack exchange,提问作者user19409250

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 20:27:27