Browse Source

设置mtu值,等待发送回调继续发送下一条消息

weicu
fdp 1 year ago
parent
commit
1ac4837448
  1. 268
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

268
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -128,6 +128,11 @@ object BleService {
private val MAX_RETRY_COUNT = 1 // 最大重试次数
private val RETRY_DELAY = 30L // 重试延迟时间(毫秒)
// MTU相关变量
private var currentMtu = 23 // 默认MTU值
private var maxMtu = 517 // 最大MTU值 (512 + 5字节头部)
private var isWaitingForWriteCallback = false // 是否正在等待写入回调
// 初始化状态
private var isInitialized = false
@ -859,6 +864,9 @@ object BleService {
// 设置主服务的通知
setupNotifications(g, notifyChar)
// 在服务发现完成后,请求最大MTU
requestMaxMtu(g)
}
@ -915,10 +923,10 @@ object BleService {
override fun onCharacteristicWrite(
g: BluetoothGatt, c: BluetoothGattCharacteristic, status: Int
) {
Log.i(
TAG,
"写入 ${if (status == BluetoothGatt.GATT_SUCCESS) "成功" else "失败"}"
)
Log.i(
TAG,
"写入 ${if (status == BluetoothGatt.GATT_SUCCESS) "成功" else "失败"}"
)
}
override fun onDescriptorWrite(
@ -927,6 +935,22 @@ object BleService {
if (status == BluetoothGatt.GATT_SUCCESS)
Log.i(TAG, "通知描述符写入成功: ${d.characteristic.uuid}")
}
/**
* MTU变化回调 - 处理MTU协商结果
*/
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
super.onMtuChanged(gatt, mtu, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
currentMtu = mtu
Log.i(TAG, "MTU协商成功: $mtu 字节")
// 更新音频块大小,预留5字节的ATT头部
val newChunkSize = (mtu - 5).coerceAtMost(512)
Log.i(TAG, "更新音频块大小为: $newChunkSize 字节")
} else {
Log.w(TAG, "MTU协商失败: status=$status,使用默认MTU: $currentMtu")
}
}
}
/**
@ -943,6 +967,23 @@ object BleService {
desc?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
g.writeDescriptor(desc)
}
/**
* 请求最大MTU值
* @param gatt BluetoothGatt实例
*/
private fun requestMaxMtu(gatt: BluetoothGatt) {
try {
val result = gatt.requestMtu(maxMtu)
if (result) {
Log.i(TAG, "请求MTU $maxMtu 成功")
} else {
Log.w(TAG, "请求MTU $maxMtu 失败")
}
} catch (e: Exception) {
Log.e(TAG, "请求MTU异常: ${e.message}", e)
}
}
/**
* 处理接收到的音频数据
@ -1039,9 +1080,9 @@ object BleService {
/**
* 启动音频数据发送线程
* 启动音频数据发送线程(优化版本)
*/
private fun startAudioSendThread() {
private fun startAudioSendThread() {
if (audioSendThread?.isAlive == true) {
Log.d(TAG, "音频发送线程已在运行")
return
@ -1056,11 +1097,9 @@ object BleService {
// 从队列中取出音频数据块
val audioChunk = audioSendQueue.poll(100, TimeUnit.MILLISECONDS)
if (audioChunk != null) {
// 发送音频数据块
sendAudioChunk(audioChunk)
// 控制发送频率,避免蓝牙缓冲区溢出
if (audioChunk != null&&!isWaitingForWriteCallback) {
// 发送音频数据块并等待回调
sendAudioChunkWithCallback(audioChunk)
Thread.sleep(AUDIO_SEND_INTERVAL)
}
} catch (e: InterruptedException) {
@ -1088,96 +1127,133 @@ object BleService {
Log.i(TAG, "音频发送线程已停止,队列和缓冲区已清空")
}
/**
* 发送音频数据块到设备(带重发机制)
/**
* 发送音频数据块并等待写入回调(新方法)
* @param chunk 要发送的音频数据块
* @param retryCount 当前重试次数
*/
private fun sendAudioChunk(chunk: ByteArray, retryCount: Int = 0) {
private fun sendAudioChunkWithCallback(chunk: ByteArray) {
try {
if (callWriteChar == null || bluetoothGatt == null) {
Log.e(TAG, "蓝牙连接或特征值未准备就绪")
return
}
// 检查数据块大小是否超过当前MTU限制
val maxDataSize = currentMtu - 5 // 减去ATT头部5字节
if (chunk.size > maxDataSize) {
Log.w(TAG, "数据块过大 (${chunk.size} > $maxDataSize),分片发送")
// 分片发送
sendChunksInFragments(chunk, maxDataSize)
return
}
// 在主线程中执行蓝牙写入操作
audioSendHandler.post {
try {
callWriteChar?.value = chunk
recordfile1?.saveAudioDataAsUtf8Hex(chunk, true)
val isSuccess = bluetoothGatt?.writeCharacteristic(callWriteChar)
if (isSuccess == true) {
recordfile1?.saveAudioDataToWav("发送成功,大小: ${chunk.size} 字节")
// 累加发送成功的字节数
bytesSentInCurrentSecond += chunk.size
Log.d(TAG, "成功发送音频数据块,大小: ${chunk.size} 字节")
} else {
recordfile1?.saveAudioDataToWav("发送失败,大小: ${chunk.size} 字节")
Log.e(TAG, "发送音频数据块失败,大小: ${chunk.size} 字节")
// 详细的失败原因分析
val failureReason = StringBuilder("发送音频数据块失败,大小: ${chunk.size} 字节")
isWaitingForWriteCallback = true
// 检查蓝牙连接状态
if (bluetoothGatt == null) {
failureReason.append(" - 原因: BluetoothGatt为空")
} else {
// 检查连接状态
val State = bluetoothManager.getConnectionState(
bluetoothGatt!!.device,
BluetoothProfile.GATT
)
when (State) {
BluetoothProfile.STATE_DISCONNECTED -> {
failureReason.append(" -发送失败 原因: 设备已断开连接")
}
BluetoothProfile.STATE_CONNECTING -> {
failureReason.append(" -发送失败 原因: 设备正在连接中")
}
BluetoothProfile.STATE_DISCONNECTING -> {
failureReason.append(" -发送失败 原因: 设备正在断开连接")
}
BluetoothProfile.STATE_CONNECTED -> {
// 连接正常,检查其他原因
if (callWriteChar == null) {
failureReason.append(" -发送失败 原因: 写入特征值为空")
} else {
// 检查特征值属性
val properties = callWriteChar!!.properties
if ((properties and BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 &&
(properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
failureReason.append(" -发送失败 原因: 特征值不支持写入操作")
} else if (chunk.size > 512) { // BLE MTU通常限制
failureReason.append(" -发送失败 原因: 数据块过大 (${chunk.size} > 512字节)")
} else {
failureReason.append(" -发送失败 原因: 未知错误,可能是设备忙碌或缓冲区满")
}
}
}
else -> {
failureReason.append(" -发送失败 原因: 未知连接状态($connectionState)")
}
}
}
callWriteChar?.value = chunk
recordfile1?.saveAudioDataAsUtf8Hex(chunk, true)
val isSuccess = bluetoothGatt?.writeCharacteristic(callWriteChar)
// 检查蓝牙适配器状态
if (bluetoothAdapter?.isEnabled != true) {
failureReason.append(" - 蓝牙适配器未启用")
if (isSuccess == true) {
isWaitingForWriteCallback = false
recordfile1?.saveAudioDataToWav("发送成功,大小: ${chunk.size} 字节")
bytesSentInCurrentSecond += chunk.size
Log.d(TAG, "成功发送音频数据块,大小: ${chunk.size} 字节,等待写入回调...")
} else {
isWaitingForWriteCallback = false
recordfile1?.saveAudioDataToWav("发送失败,大小: ${chunk.size} 字节")
Log.e(TAG, "发送音频数据块失败,大小: ${chunk.size} 字节")
analyzeWriteFailure(chunk)
}
Log.e(TAG, failureReason.toString())
}
} catch (e: Exception) {
Log.e(TAG, "发送音频数据块异常: ${e.message}", e)
Log.e(TAG, "发送音频数据块异常: ${e.message}", e)
}
}
} catch (e: Exception) {
Log.e(TAG, "发送音频数据块异常: ${e.message}", e)
}
}
/**
* 分片发送大数据块
* @param data 原始数据
* @param maxChunkSize 每片的最大大小
*/
private fun sendChunksInFragments(data: ByteArray, maxChunkSize: Int) {
var offset = 0
while (offset < data.size) {
val remainingSize = data.size - offset
val chunkSize = minOf(remainingSize, maxChunkSize)
val fragment = data.copyOfRange(offset, offset + chunkSize)
// 递归调用发送单个片段
sendAudioChunkWithCallback(fragment)
offset += chunkSize
}
}
/**
* 分析写入失败的原因(从原sendAudioChunk方法提取)
* @param chunk 失败的数据块
*/
private fun analyzeWriteFailure(chunk: ByteArray) {
val failureReason = StringBuilder("发送音频数据块失败,大小: ${chunk.size} 字节")
// 检查蓝牙连接状态
if (bluetoothGatt == null) {
failureReason.append(" - 原因: BluetoothGatt为空")
} else {
// 检查连接状态
val state = bluetoothManager.getConnectionState(
bluetoothGatt!!.device,
BluetoothProfile.GATT
)
when (state) {
BluetoothProfile.STATE_DISCONNECTED -> {
failureReason.append(" -发送失败 原因: 设备已断开连接")
}
BluetoothProfile.STATE_CONNECTING -> {
failureReason.append(" -发送失败 原因: 设备正在连接中")
}
BluetoothProfile.STATE_DISCONNECTING -> {
failureReason.append(" -发送失败 原因: 设备正在断开连接")
}
BluetoothProfile.STATE_CONNECTED -> {
// 连接正常,检查其他原因
if (callWriteChar == null) {
failureReason.append(" -发送失败 原因: 写入特征值为空")
} else {
// 检查特征值属性
val properties = callWriteChar!!.properties
if ((properties and BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 &&
(properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
failureReason.append(" -发送失败 原因: 特征值不支持写入操作")
} else if (chunk.size > currentMtu - 5) {
failureReason.append(" -发送失败 原因: 数据块过大 (${chunk.size} > ${currentMtu - 5}字节)")
} else {
failureReason.append(" -发送失败 原因: 未知错误,可能是设备忙碌或缓冲区满")
}
}
}
else -> {
failureReason.append(" -发送失败 原因: 未知连接状态($state)")
}
}
}
// 检查蓝牙适配器状态
if (bluetoothAdapter?.isEnabled != true) {
failureReason.append(" - 蓝牙适配器未启用")
}
Log.e(TAG, failureReason.toString())
}
// ======================================================================================================
// 设备控制方法 - 使用新的指令发送器
// ======================================================================================================
@ -1386,6 +1462,40 @@ object BleService {
fun getBluetoothAdapter(): BluetoothAdapter? {
return bluetoothAdapter
}
/**
* 获取当前MTU值
* @return 当前MTU值
*/
fun getCurrentMtu(): Int {
return currentMtu
}
/**
* 获取可用的数据传输大小(MTU减去头部)
* @return 可用的数据传输大小
*/
fun getAvailableDataSize(): Int {
return currentMtu - 5 // 减去ATT头部5字节
}
/**
* 手动请求MTU协商
* @param mtu 请求的MTU值
* @return 是否成功发起请求
*/
fun requestMtu(mtu: Int): Boolean {
return bluetoothGatt?.let { gatt ->
try {
val result = gatt.requestMtu(mtu.coerceIn(23, 517))
Log.i(TAG, "手动请求MTU $mtu: $result")
result
} catch (e: Exception) {
Log.e(TAG, "手动请求MTU异常: ${e.message}", e)
false
}
} ?: false
}
// ======================================================================================================

Loading…
Cancel
Save