Android 12如何通过听筒播放音频?AudioManager设置不生效问题
问题根因
这不是模拟器bug,是Android 12(API Level 31)对音频路由策略的官方变更导致原有逻辑失效,核心原因有两个:
- Android 12及以上修改音频输出路径需要新增
MODIFY_AUDIO_ROUTING危险权限,仅声明权限不动态申请无法操作音频路由 - PeerJS底层依赖的WebRTC内核默认会抢占音频路由优先级,主动切换到扬声器输出,会覆盖直接对
AudioManager的属性设置
修复方案
1. 补充权限声明与动态申请
首先在AndroidManifest.xml中添加所需权限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- Android 12及以上新增必需权限 --> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
在初始化音频功能前,动态申请MODIFY_AUDIO_ROUTING权限,该权限属于Android 12新增的危险权限,必须用户授权后才能生效。
2. 适配Android 12新音频路由API
Android 12开始废弃了直接修改isSpeakerphoneOn的路由控制逻辑,改用setCommunicationDevice接口控制通话音频输出设备,替换原有代码如下:
private fun setAudioOutput(useSpeaker: Boolean) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val targetDevice = if (useSpeaker) { // 找扬声器设备 am.getDevices(AudioManager.GET_DEVICES_OUTPUTS).firstOrNull { it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER } } else { // 找听筒设备 am.getDevices(AudioManager.GET_DEVICES_OUTPUTS).firstOrNull { it.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE } } targetDevice?.let { am.setCommunicationDevice(it) } } else { // 低版本沿用原有逻辑 am.isSpeakerphoneOn = useSpeaker } } private fun initAudio(){ am = getSystemService(AUDIO_SERVICE) as AudioManager volumeControlStream = AudioManager.STREAM_VOICE_CALL am.mode = AudioManager.MODE_IN_COMMUNICATION // 切换到听筒输出 setAudioOutput(false) } private fun toggleSpeakerMode(){ val currentIsSpeaker = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { am.communicationDevice?.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER } else { am.isSpeakerphoneOn } setAudioOutput(!currentIsSpeaker) }
3. 禁用WebRTC默认音频路由抢占
PeerJS底层的WebRTC内核默认会自动调整音频路由,需要在WebView初始化时注入JS禁用该逻辑:
webView.evaluateJavascript(""" if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { const originalGetUserMedia = navigator.mediaDevices.getUserMedia; navigator.mediaDevices.getUserMedia = function(constraints) { return originalGetUserMedia.call(this, constraints).then(stream => { stream.getAudioTracks().forEach(track => { track.applyConstraints({ autoGainControl: false, echoCancellation: true, noiseSuppression: true }); }); return stream; }); } } """.trimIndent(), null)
注意事项
- Android 12及以上不要同时混用
setCommunicationDevice和isSpeakerphoneOn接口,避免路由逻辑冲突 - 若修改后模拟器仍异常可以用真机验证,少数Android 12模拟器镜像存在音频路由模拟不完善的问题,但绝大多数同类问题都是API适配和权限问题导致
内容的提问来源于stack exchange,提问作者Sujith S Manjavana
相关产品推荐
相关产品推荐

