需求背景
要求某些場景已經插入耳機或連接了藍牙耳機,需要使用揚聲器播放音頻
實現調研
有線耳機的情況
排除藍牙耳機情況,使用有線耳機,切換時只需要打開/關閉揚聲器即可。這樣就可以實現在插入耳機的情況下,使用揚聲器播放。 代碼如下:
//切換爲揚聲器
AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
audioManager.setMicrophoneMute(false);
audioManager.setSpeakerphoneOn(true);//使用揚聲器外放,即使已經插入耳機
//setVolumeControlStream(AudioManager.STREAM_MUSIC);//控制聲音的大小
audioManager.setMode(AudioManager.STREAM_MUSIC);
//切換爲耳機
mAudioManager.setSpeakerphoneOn(false);
另外,播放音頻Android提供了兩套Api,一套爲MediaPlayer,偏上層。另一套AudioTrack偏底層。實驗發現,在使用AudioTrack時需要添加增加AUDIO_SETTING權限。所以爲了保險起見,需要加上如下權限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"></uses-permission>
藍牙耳機
起初認爲藍牙耳機與有線耳機效果一樣,但發現使用上述有線耳機的切換代碼,效果爲:切換至揚聲器可從揚聲器播放,關閉揚聲器後。藍牙耳機中無聲音,揚聲器也無聲音。查找資料發現,需要手動打開藍牙耳機,建立連接。代碼如下:
- 連接藍牙耳機,關閉揚聲器
/**
* 關閉揚聲器
*/
private void offSpeaker() {
if (mAudioManager == null) {
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
Log.i("zxg", "isBluetoothSco 2:" + mAudioManager.isBluetoothScoOn());
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
//如果有藍牙耳機設備連接,打開Sco通道使用藍牙耳機播放音頻
if (isBluetoothHeadsetConnected()) {
Log.i("zxg", "need start BluetoothSco");
mAudioManager.startBluetoothSco();
mAudioManager.setBluetoothScoOn(true);
}
//關閉揚聲器
mAudioManager.setSpeakerphoneOn(false);
}
- 關閉藍牙Sco通道,打開揚聲器
/**
* 打開揚聲器
*/
private void speaker() {
if (mAudioManager == null) {
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
// mAudioManager.setMicrophoneMute(false);
//關閉Sco
if (isBluetoothHeadsetConnected()) {
mAudioManager.setBluetoothScoOn(false);
mAudioManager.stopBluetoothSco();
}
//打開揚聲器
mAudioManager.setSpeakerphoneOn(true);
mAudioManager.setMode(AudioManager.STREAM_MUSIC);
Log.i("zxg", "isBluetoothSco 1:" + mAudioManager.isBluetoothScoOn());
}
- 判斷是否有藍牙耳機與設備連接
/**
* 判斷藍牙耳機是否連接
* @return
*/
private boolean isBluetoothHeadsetConnected() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (BluetoothProfile.STATE_CONNECTED == adapter.getProfileConnectionState(BluetoothProfile.HEADSET)) {
return true;
}
return false;
}
- 註冊廣播監聽
需要注意的是,打開/關閉Sco是異步的,並不是馬上完成的,所以我們需要監聽系統廣播,收到相關廣播後繼續進行後續邏輯操作。在具體邏輯代碼中可以通過mAudioManager.isBluetoothScoOn()判斷Sco狀態,以及在廣播中更新標記位來記錄Sco狀態
/**
* 監聽Sco變化廣播
*/
private void registerBluetoothBroadCast() {
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -100);
Log.i("zxg", "EXTRA_SCO_AUDIO_STATE:" + state);
}
}, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
}
- 添加權限
操作藍牙需要申請如下權限
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
調研結果
使用以上代碼嵌入出行司機端中,使用藍牙/有線耳機切換,播放百度TTS語音。可以實現切換效果:即在連接藍牙耳機/插入有線耳機時,可以通過揚聲器播放音頻。