Android蓝牙开发:Wear手表被误识别为连接蓝牙耳机问题咨询
问题1修复方案
你最初的逻辑仅判断HEADSET profile连接状态,会把所有支持HFP/HSP协议的设备(包括带通话功能的智能手表)全部判定为耳机,只需要在原有逻辑基础上补一层设备主类校验即可,不需要重构整体逻辑。
注意你临时方案里的掩码写法属于歪打正着:BluetoothClass.Device.Major.UNCATEGORIZED的实际值就是0x1F00,也就是系统隐藏的主设备类掩码常量,直接硬编码这个固定值(蓝牙协议规定的标准值,所有安卓版本不会变动)做位运算即可,不需要反射获取隐藏常量。
修复后的判断逻辑可以直接替换你原来的if判断:
// 放在onCreate里即可,改动量极小 if (bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET) == BluetoothProfile.STATE_CONNECTED) { val majorClassMask = 0x1F00 // 蓝牙标准主设备类掩码,对应系统隐藏常量BluetoothClass.MAJOR_BIT_MASK var hasRealAudioDevice = false bluetoothAdapter.getProfileProxy(this, object : ServiceListener { override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { proxy.connectedDevices.forEach { device -> val majorClass = device.bluetoothClass.deviceClass and majorClassMask if (majorClass == BluetoothClass.Device.Major.AUDIO_VIDEO) { hasRealAudioDevice = true return@forEach } } bluetoothAdapter.closeProfileProxy(profile, proxy) if (hasRealAudioDevice) sendSpeechToSpeaker() else sendSpeechToPhone() } override fun onServiceDisconnected(profile: Int) {} }, BluetoothProfile.HEADSET) } else { sendSpeechToPhone() }
问题2更优实现方案
比手动遍历蓝牙设备、过滤设备类型简便得多,且兼容性更好的方案是放弃自行维护蓝牙设备状态,直接从系统音频管理器拿真实的输出设备列表:
智能手表虽然会注册HEADSET profile,但绝大多数不支持A2DP音频输出协议,系统本身就不会把TTS、媒体类音频路由到这类设备上,你完全不需要自己做设备类型判断,系统已经帮你完成了所有过滤逻辑。
实现代码非常短,不需要处理蓝牙profile代理的注册、释放逻辑:
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager // 直接查询当前实际生效的音频输出设备 val isBluetoothAudioConnected = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS).any { // 普通蓝牙音箱/耳机走A2DP,通话类蓝牙音频走SCO,根据你的TTS场景选择即可 it.type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP || it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO } if (isBluetoothAudioConnected) sendSpeechToSpeaker() else sendSpeechToPhone()
如果需要实时监听音频设备切换(比如用户中途连接/断开蓝牙音箱),直接注册AudioManager.AudioDeviceCallback即可,不需要监听蓝牙连接广播,也不需要处理不同厂商手表的设备类兼容问题。
注意:这个方案完全规避了不同厂商设备蓝牙类上报不规范的问题——不管是智能手表、蓝牙手环、甚至是蓝牙自拍杆这类注册了奇怪profile的设备,只要系统没把它列为音频输出设备,就不会触发误判,比自己解析蓝牙设备类靠谱得多。
内容的提问来源于stack exchange,提问作者RatherBeSailing

