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

Android AOA模式USB通信:重启APP后接收线程失效问题

Android AOA模式重启APP后无法接收PC数据的解决方案

采用AOA模式与PC通信的Android应用,关闭重启后设备能向PC发送数据,但监听线程无法接收,仅能通过重新插拔USB恢复。排查发现重启后附件已开启,但输入流处于异常状态导致PC发送超时。

核心问题分析

  • 资源释放不彻底:关闭附件时未确保读取协程完全终止,且未清空附件引用,导致重新初始化时出现资源冲突
  • 协程取消逻辑不完善:未处理协程取消时的异常,且isReading状态未及时更新,导致线程残留
  • 初始化时未清理旧资源:重新打开附件前未确保之前的流、文件描述符等完全释放

解决方案及修改代码

1. 完善资源清理逻辑(修改closeAccessory函数)

确保协程完全终止,清空所有相关引用:

fun closeAccessory() {
    // 先终止读取循环
    isReading = false
    // 取消并等待协程结束,避免资源竞争
    if (::readjob.isInitialized) {
        readjob.cancel()
        runBlocking {
            readjob.join()
        }
    }

    // 关闭输入流
    try {
        mInputStream?.close()
    } catch (e: IOException) {
        Log.i(TAG, "Error closing inputStream", e)
        logInfo(TAG, "Error closing inputStream")
    } finally {
        mInputStream = null
    }

    // 关闭输出流
    try {
        mOutputStream?.close()
    } catch (e: IOException) {
        Log.i(TAG, "Error closing outputStream", e)
        logInfo(TAG, "Error closing outputStream")
    } finally {
        mOutputStream = null
    }

    // 关闭文件描述符
    try {
        mFileDescriptor?.close()
    } catch (e: IOException) {
        Log.i(TAG, "Error closing fileDescriptor", e)
        logInfo(TAG, "Error closing fileDescriptor")
    } finally {
        mFileDescriptor = null
    }

    // 清空附件引用
    mAccessory = null
}

2. 优化初始化逻辑(修改openAccessory函数)

初始化前先清理旧资源,处理协程取消异常,优化读取逻辑:

private fun openAccessory(accessory: UsbAccessory) {
    // 初始化前确保旧资源完全清理
    closeAccessory()

    mFileDescriptor = usbManager.openAccessory(accessory)
    if (mFileDescriptor != null) {
        mAccessory = accessory
        val fd = mFileDescriptor!!.fileDescriptor
        mInputStream = FileInputStream(fd)
        mOutputStream = FileOutputStream(fd)

        logInfo(Companion.TAG, "Accessory opened")

        readjob = GlobalScope.launch(Dispatchers.IO) {
            isReading = true
            try {
                while (isReading) {
                    val buffer = ByteArray(1000)
                    val readBytes = mInputStream!!.read(buffer)
                    // 仅处理实际读取到的数据,避免无效操作
                    if (readBytes > 0) {
                        getIncomingData(buffer.copyOf(readBytes))
                        logInfo(Companion.TAG, "Read $readBytes bytes successfully")
                    }
                }
            } catch (e: IOException) {
                logInfo(Companion.TAG, "Read failed: $e")
            } catch (e: CancellationException) {
                // 协程正常取消,无需报错
                logInfo(Companion.TAG, "Read job cancelled")
            } finally {
                isReading = false
                // 异常或取消时自动清理资源
                closeAccessory()
            }
        }
    } else {
        logInfo(Companion.TAG, "Accessory open fail")
    }
}

额外检查项

  • 确认AndroidManifest中已声明AOA相关配置:
    <uses-feature android:name="android.hardware.usb.accessory" />
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
    </intent-filter>
    <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
        android:resource="@xml/accessory_filter" />
    
  • 确保USB附件广播接收器正确处理连接/断开事件,避免重复初始化或资源泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 21:57:33