You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

调用AudioManager.setSpeakerphoneOn()触发Throwable致崩溃问题求助

AudioManager.setSpeakerphoneOn() 触发Throwable的常见原因分析

我之前做VoIP类应用的时候也碰到过一模一样的问题——代码逻辑看起来特别简单,但就是会在部分设备上触发这个Throwable日志,偶尔还会导致崩溃。结合当时的排查经验,给你几个可能的方向:

1. 音频焦点没处理好

Android的音频系统对焦点管控特别严,如果调用setSpeakerphoneOn()的时候,你的App还没拿到对应的音频焦点,或者系统里有其他音频组件(比如正在通话、后台放音乐)占着音频资源,就很容易触发这个异常。

比如你在MediaPlayerWrapper初始化的时候,可能还没完成音频焦点的请求,就直接切换扬声器状态,系统会因为音频状态不一致抛出Throwable。建议在调用audioManager.isSpeakerphoneOn = value之前,先通过AudioManager.requestAudioFocus()获取焦点,并且处理好焦点丢失的回调逻辑。

2. 厂商ROM的音频策略限制

不同安卓厂商的定制ROM会对音频系统做各种修改,有些设备会限制非通话场景下直接切换扬声器状态。比如部分机型在媒体播放模式下,强制要求通过音频路由的方式切换,而不是直接调用setSpeakerphoneOn()

如果是通话类应用,你可以试试先把音频模式改成AudioManager.MODE_IN_COMMUNICATION,再切换扬声器状态,很多时候能解决这个厂商定制带来的问题。

3. 多线程下的状态竞态问题

你的loudspeakerState是可空类型,虽然做了value != null的判断,但如果是多线程场景,可能出现状态冲突:比如刚把field设为某个值,紧接着另一个线程又修改它,导致setSpeakerphoneOn()被连续调用,系统来不及处理状态切换就抛出异常。

建议给这个属性的setter加个线程锁,或者用AtomicBoolean来管理状态,避免并发修改导致的冲突。

4. 系统版本兼容性问题

在Android 10(API 29)及以上,音频系统的权限和状态管理有不少变化,比如新增了ACCESS_NOTIFICATION_POLICY权限,对音频路由的控制也更严格。如果你的应用没适配这些变化,在部分新机型上就容易出问题。

可以检查下你的targetSdkVersion,确保已经适配了对应版本的音频权限和API调用规范。


你的问题日志参考

I/AudioManager: setSpeakerphoneOn false
java.lang.Throwable
at android.media.AudioManager.setSpeakerphoneOn(AudioManager.java:1235)
at com.mj.callapp.device.media.MediaPlayerWrapper.setLoudspeakerState(MediaPlayerWrapper.kt:47)
at com.mj.callapp.device.media.MediaPlayerWrapper.(MediaPlayerWrapper.kt:75)

你的核心代码参考

var loudspeakerState: Boolean? = null
set(value) {
    if (value != null && value != field) {
        field = value
        audioManager.isSpeakerphoneOn = value //line 47
        loudspeakerStateSubject.onNext(value)
    }
}

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

火山引擎 最新活动