手机音频的输出有外放(Speaker)、听筒(Telephone Receiver)、有线耳机(WiredHeadset)、蓝牙音箱(Bluetooth A2DP)等输出设备。在平时,电话免提、插拔耳机、连接断开蓝牙设备等操作系统都会自动切换Audio音频到相应的输出设备上。比如电话免提就是从听筒切换到外放扬声器,插入耳机就是从外放切换到耳机。
场景需求
Android系统自动切换的这些策略,并不能全部满足我们的产品需求,比如音乐App需要对听歌时拔出耳机的操作进行阻止(暂停播放),防止突然切换到外放导致尴尬。
最近项目需求希望即使在连接蓝牙音箱的情况下,仍旧使用手机外放播放音频
。这就需要强制切换Audio输出通道,打破系统原有的策略。
查阅资料,看到了Android中可以通过AudioManager
查询、切换当前Audio输出通道,并且在Audio输出发生变化时,捕获并处理这种变化。
首先提醒下大家,使用下面的方法时,需要添加权限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
Audio输出状态查询
AudioManager 提供的下列方法可以用来查询当前Audio输出的状态:
isBluetoothA2dpOn()
:检查A2DPAudio音频输出是否通过蓝牙耳机;isSpeakerphoneOn()
:检查扬声器是否打开;isWiredHeadsetOn()
:检查线控耳机是否连着;注意这个方法只是用来判断耳机是否是插入状态,并不能用它的结果来判定当前的Audio是通过耳机输出的,这还依赖于其他条件。setSpeakerphoneOn(boolean on)
:直接选择外放扬声器发声;setBluetoothScoOn(boolean on)
:要求使用蓝牙SCO耳机进行通讯;
此处根据这篇文章简单地介绍一下蓝牙耳机的两种链路,A2DP及SCO。android的api表明:
- A2DP:是一种单向的高品质音频数据传输链路,通常用于播放立体声音乐;
- SCO: 则是一种双向的音频数据的传输链路,该链路只支持8K及16K单声道的音频数据,只能用于普通语音的传输,若用于播放音乐那就只能呵呵了。
两者的主要区别是:A2DP只能播放,默认是打开的,而SCO既能录音也能播放,默认是关闭的。 如果要录音肯定要打开sco啦,因此调用上面的setBluetoothScoOn(boolean on)
就可以通过蓝牙耳机录音、播放音频了,录完、播放完记得要关闭。
另外,在Android系统中通过AudioManager.setMode()
方法来管理播放模式。在setMode()
方法中有以下几种对应不同的播放模式:
MODE_NORMAL
: 普通模式,既不是铃声模式也不是通话模式MODE_RINGTONE
: 铃声模式MODE_IN_CALL
: 通话模式MODE_IN_COMMUNICATION
: 通信模式,包括音/视频,VoIP通话.(3.0加入的,与通话模式类似)
在设置播放模