Android 音频设备管理
Android 音頻設(shè)備管理
一、簡(jiǎn)介
在即使語(yǔ)音或者實(shí)時(shí)視頻通話中,時(shí)常需要提供以下功能:
-
手動(dòng)切換聽(tīng)筒或者揚(yáng)聲器;
-
連接藍(lán)牙時(shí),音頻需轉(zhuǎn)到藍(lán)牙;
-
連接有線耳機(jī)時(shí),音頻需轉(zhuǎn)到有線耳機(jī);
Android實(shí)現(xiàn)以上功能需要用到以下幾個(gè)類(lèi):
-
AudioManager:提供對(duì)音量和振鈴模式控制的訪問(wèn);
-
BluetoothManager:藍(lán)牙管理器,用于獲取BluetoothAdapter藍(lán)牙本地適配器的實(shí)例,并進(jìn)行整體藍(lán)牙管理;
-
BluetoothAdapter:藍(lán)牙本地適配器,用于管理藍(lán)牙的搜索、連接等功能;
-
BluetoothHeadset:藍(lán)牙免提的配置文件,需要又聽(tīng)又說(shuō)則需要使用此配置類(lèi)。
藍(lán)牙配置文件還有兩個(gè):
BluetoothA2dp:藍(lán)牙立體聲音傳輸配置文件,需要只聽(tīng)可以使用此配置類(lèi)。
BluetoothHealth:藍(lán)牙健康設(shè)備配置文件,需要與健康設(shè)備(如心率檢測(cè)儀、血糖儀、溫度計(jì)、臺(tái)秤等)進(jìn)行通信,則可以使用此配置類(lèi)。
這兩個(gè)藍(lán)牙配置文件,這里用不上,所以就簡(jiǎn)單介紹下,有興趣的可以自行學(xué)習(xí)。
二、相關(guān)類(lèi)用法介紹
1. AudioManager
1)獲取此類(lèi)實(shí)例
通過(guò)Context中的getSystemService接口獲取
private AudioManager mAudioManager = null; mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);2)設(shè)置音頻模式
AudioManager中的setMode方法,用于設(shè)置當(dāng)前的音頻的播放模式。以下是幾種常用音頻模式:
- MODE_NORMAL : 普通模式,既不是鈴聲模式也不是通話模式
- MODE_RINGTONE : 鈴聲模式
- MODE_IN_CALL : 通話模式
- MODE_IN_COMMUNICATION : 通信模式,包括音/視頻,VoIP通話.(3.0加入的,與通話模式類(lèi)似)
即時(shí)語(yǔ)言一般使用MODE_IN_COMMUNICATION 通信模式,Android 3.0之前使用MODE_IN_CALL 通話模式。實(shí)際開(kāi)發(fā)過(guò)程中,有一些機(jī)型在3.0之后也需要使用MODE_IN_CALL 通話模式,但只是少數(shù)。
if(mAudioManager != null){mAudioManager.setMOde(AudioManager.MODE_IN_COMMUNICATION); }在使用完后,需要恢復(fù)原來(lái)的模式,這里我默認(rèn)恢復(fù)到MODE_NORMAL普通模式。
if(mAudioManager != null){mAudioManager.setMOde(AudioManager.MODE_NORMAL); }最好保證在同個(gè)進(jìn)程中調(diào)用setMode方法。如果在不同進(jìn)程中調(diào)用setMode方法,系統(tǒng)會(huì)將上一個(gè)調(diào)用setMode的進(jìn)程的藍(lán)牙SCO數(shù)據(jù)斷開(kāi),就會(huì)導(dǎo)致藍(lán)牙數(shù)據(jù)斷連。
3)切換聽(tīng)筒、揚(yáng)聲器
AudioManager中的setSpeakerphoneOn方法,用于設(shè)置揚(yáng)聲器是否打開(kāi)。
// 設(shè)置聽(tīng)筒出聲 if(mAudioManager != null){mAudioManager.setSpeakerphoneOn(false); }// 設(shè)置揚(yáng)聲器出聲 if(mAudioManager != null){mAudioManager.setSpeakerphoneOn(true); }并且可以使用isSpeakerphoneOn方法判斷當(dāng)前揚(yáng)聲器是否打開(kāi)
if(mAudioManager != null){if(mAudioManager.isSpeakerphoneOn()){// 目前是揚(yáng)聲器出聲}else{// 目前是聽(tīng)筒出聲} }切換音頻到有線耳機(jī)的操作跟切換聽(tīng)筒的操作一致,只需調(diào)用setSpeakerphoneOn(false); 即可
2. BluetoothAdapter
1)獲取此類(lèi)實(shí)例
- sdk版本大于等于18時(shí),使用BluetoothManager獲取BluetoothAdapter
- sdk版本小于18時(shí),使用BluetoothAdapter.getDefaultAdapter()獲取
2)判斷本地藍(lán)牙適配器是否打開(kāi)
BluetoothAdapter 中的isEnabled方法,用于判斷手機(jī)本地的藍(lán)牙適配器是否打開(kāi)。如果返回值為false,則代表手機(jī)本地的適配器沒(méi)有打開(kāi),也就不可能有藍(lán)牙連接。
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnable()){// 藍(lán)牙適配器已經(jīng)打開(kāi) }else{// 手機(jī)不支持藍(lán)牙或沒(méi)有打開(kāi)藍(lán)牙適配器 }3. BluetoothHeadset
1)獲取此類(lèi)實(shí)例
- 首先需要進(jìn)行藍(lán)牙協(xié)議切換,調(diào)用BluetoothAdapter 的getProfileProxy方法,將藍(lán)牙協(xié)議切換為BluetoothProfile.HEADSET協(xié)議。getProfileProxy方法需要傳入的參數(shù):當(dāng)前上下文Context,藍(lán)牙協(xié)議監(jiān)聽(tīng)回調(diào)函數(shù)BluetoothProfile.ServiceListener,需要切換的藍(lán)牙協(xié)議BluetoothProfile.HEADSET;
- 接著在藍(lán)牙協(xié)議切換成功的監(jiān)聽(tīng)回調(diào)函數(shù)中賦值;
2)切換藍(lán)牙耳機(jī)(HEADSET協(xié)議)
一般在收到藍(lán)牙協(xié)議切換成功后,才進(jìn)行藍(lán)牙耳機(jī)的切換。也就是在上一點(diǎn)的回調(diào)函數(shù)中獲取HEADSET協(xié)議的配置后,就進(jìn)行藍(lán)牙耳機(jī)的切換。
切換藍(lán)牙耳機(jī)使用AudioManager中三個(gè)方法:setSpeakerphoneOn、startBluetooth、ScosetBluetoothScoOn。
// 切換為藍(lán)牙耳機(jī) private void switchBluetoothEarphone() {if (mAudioManager == null || mBluetoothAdapter == null || mBluetoothHeadset == null) {return;}if(!mBluetoothAdapter.isEnabled()){return;}// 先將揚(yáng)聲器關(guān)閉mAudioManager.setSpeakerphoneOn(false);// 打開(kāi)藍(lán)牙SCO連接mAudioManager.startBluetoothSco();mAudioManager.setBluetoothScoOn(true); }若在藍(lán)牙耳機(jī)使用途中,想要強(qiáng)制切換到聽(tīng)筒或揚(yáng)聲器時(shí),需要先將藍(lán)牙的SCO連接關(guān)閉,再切換聽(tīng)筒或揚(yáng)聲器。
// 切換為揚(yáng)聲器 private void switchSpeakerphone() {if (mAudioManager == null) {return;}if (mBluetoothAdapter != null && mBluetoothHeadset != null) {mAudioManager.setBluetoothScoOn(false);mAudioManager.stopBluetoothSco();}mAudioManager.setSpeakerphoneOn(true); }// 切換為聽(tīng)筒 private void switchEarphone() {if (mAudioManager == null) {return;}if (mBluetoothAdapter != null && mBluetoothHeadset != null) {mAudioManager.setBluetoothScoOn(false);mAudioManager.stopBluetoothSco();}mAudioManager.setSpeakerphoneOn(false); }3)關(guān)閉此類(lèi)實(shí)例
在使用完BluetoothHeadSet時(shí),需要調(diào)用 BluetoothAdapter的closeProfileProxy方法將協(xié)議關(guān)閉。協(xié)議成功關(guān)閉后,依舊會(huì)走之前注冊(cè)的回調(diào)函數(shù)的onServiceDisconnected方法。
// 關(guān)閉協(xié)議 if (mBluetoothAdapter != null && mBluetoothHeadset != null) {mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); }三、監(jiān)聽(tīng)耳機(jī)的連接與斷連
系統(tǒng)在耳機(jī)狀態(tài)改變時(shí)會(huì)發(fā)送相應(yīng)的廣播信號(hào)。所以監(jiān)聽(tīng)耳機(jī)的插入拔出或者連接斷連,需要注冊(cè)一個(gè)廣播BroadcastReceiver。廣播的使用如下代碼:
/*** 注冊(cè)廣播監(jiān)聽(tīng)*/ private void registerBluetoothReceiver() {IntentFilter filter = new IntentFilter();filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); // BluetoothHeadset連接狀態(tài)filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);// 藍(lán)牙SCO的連接狀態(tài)filter.addAction(AudioManager.ACTION_HEADSET_PLUG);// 有線耳機(jī)的插入拔出if (mContext != null) {mContext.registerReceiver(mBluetoothReceiver, filter);} } /*** 廣播監(jiān)聽(tīng)設(shè)備的狀態(tài)*/ private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if (intent == null) {return;}String action = intent.getAction();BluetoothDevice dev;if (action == null) {return;}switch(action){// 判斷這個(gè)action對(duì)應(yīng)監(jiān)聽(tīng)的事件//....省略后面代碼,后續(xù)繼續(xù)展開(kāi) }} };1. 監(jiān)聽(tīng)有線耳機(jī)的插入與拔出
1)注冊(cè)廣播
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);// 有線耳機(jī)的插入拔出2)監(jiān)聽(tīng)廣播
AudioManager.ACTION_HEADSET_PLUG廣播Intent中包含有參數(shù)state,表明有線耳機(jī)設(shè)備是否插入耳機(jī)孔。
case AudioManager.ACTION_HEADSET_PLUG: {if (intent.hasExtra("state")) {int state = intent.getIntExtra("state", 0);if (state == 1) {//插入耳機(jī)switchPlugInEarphones();} else if (state == 0) {//拔出耳機(jī)switchEarphone();}}break; }在開(kāi)發(fā)中發(fā)現(xiàn),只要手機(jī)插入過(guò)一次有線耳機(jī),就算當(dāng)前沒(méi)有拔插有線耳機(jī),后面重新注冊(cè)廣播時(shí),會(huì)收到一次耳機(jī)拔出的廣播。重啟手機(jī)后,這種現(xiàn)象就會(huì)消失,但不可能每次拔插耳機(jī)就重啟手機(jī)。
2. 監(jiān)聽(tīng)藍(lán)牙耳機(jī)的連接與斷連
1)注冊(cè)廣播
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); // BluetoothHeadset連接狀態(tài) filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);// 藍(lán)牙SCO的連接狀態(tài)2)監(jiān)聽(tīng)廣播
- BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED 廣播intent包含當(dāng)前的連接設(shè)備信息dev,以及當(dāng)前的連接狀態(tài)state。切換協(xié)議后,在藍(lán)牙中途連接或者
- AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED 廣播intent包含當(dāng)前切換SCO鏈路的狀態(tài),切換成功會(huì)回調(diào)SCO_AUDIO_STATE_CONNECTED狀態(tài),如果切換失敗或者斷開(kāi),則會(huì)回調(diào)SCO_AUDIO_STATE_DISCONNECTED狀態(tài)。
3. 判斷當(dāng)前連接的是有線耳機(jī)還是藍(lán)牙耳機(jī)
- AudioManager的isWiredHeadsetOn方法可以判斷是否連接著有線耳機(jī),使用isBluetoothScoOn和isBluetoothA2dpOn方法可以判斷是否連接著藍(lán)牙耳機(jī)。
- 新版本的話推薦使用AudioManager.GET_DEVICES_OUTPUTS去獲取當(dāng)前連接的設(shè)備信息,設(shè)備信息保存在AudioDeviceInfo中。
總結(jié)
以上是生活随笔為你收集整理的Android 音频设备管理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 什么叫资源整合?
- 下一篇: 一个初学编程的草根大叔用了一个月时间冲上