Android在藍牙開發過程當中,常常須要藍牙成功鏈接後,控制像音箱,藍牙燈之類的硬件。這時候,打開手機的藍牙設置,常會看到電話音頻和媒體音頻,這兩個是什麼鬼?git
從開發Android到如今,我感受都是懵懵懂懂的,天天和硬件工程師在一塊兒。只能說算是能開發Android app,至於這些協議老是啃了忘,忘了啃。下面說一下,個人理解。若是有不對的地方,煩請留言或者私信告訴我。github
如上圖中,通常電話音頻是否支持要和嵌入式工程師確認。藍牙芯片若是不支持電話音頻,這裏就不會顯示出來了。
這裏說一下電話音頻的協議:HSP和HFP。
先看定義:api
HSP(手機規格)– 提供手機(移動電話)與耳機之間通訊所需的基本功能。
HFP(免提規格)– 在 HSP 的基礎上增長了某些擴展功能,原來只用於從固定車載免提裝置來控制移動電話。網絡
這個網上的解釋我以爲是最好理解的了。這裏要重點說一下鏈接HSP的核心代碼處理了。app
public class Hfp { public static final String TAG = "Hfp"; private BluetoothAdapter mBluetoothAdapter; private BluetoothProfile hfpProfile; private static Hfp INSTANCE; private boolean enableHfp = false; private Hfp() { mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); enableHfp = mBluetoothAdapter.getProfileProxy(XApplication.getInstance(), new ProfileListener(), BluetoothProfile.HEADSET); } public boolean isEnableHfp(){ return enableHfp; } public synchronized static Hfp getInstance() { if (INSTANCE == null) { INSTANCE = new Hfp(); } return INSTANCE; } public class ProfileListener implements BluetoothProfile.ServiceListener { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { if (profile == BluetoothProfile.HEADSET) { hfpProfile = proxy; } } @Override public void onServiceDisconnected(int profile) { Log.i(TAG, "onServiceDisconnected="+profile); } } public int getHfpState(){ if(mBluetoothAdapter != null){ return mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET); }else{ return -1; } } public BluetoothDevice getSysHfpConnected(){ List<BluetoothDevice> devices = DeviceManager.getSysPairedDevices(); for(BluetoothDevice device : devices){ if(getHfpState(device)){ return device; } } return null; } public boolean getHfpState(BluetoothDevice device){ Logg.e(TAG, "hfpProfile_-->" + hfpProfile); if(hfpProfile != null && hfpProfile.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED){ return true; } return false; }
}ide
————————————————————————————————————
估計沒有寫註釋不少人仍是雲裏霧裏的。說一下具體思路:
構造函數:Hfp()
獲取藍牙適配器:(藍牙開發中經常使用代碼)函數
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); //判斷Hsp的狀態 enableHfp = mBluetoothAdapter.getProfileProxy(XApplication.getInstance(), new ProfileListener(), BluetoothProfile.HEADSET); 注意:XApplication.getInstance() XApplication就是你的項目的application,記得綁定在一塊兒。
getHfpState(),getSysHfpConnected(),getHfpState三個方法根據實際狀況使用。
至於:public class ProfileListener implements BluetoothProfile.ServiceListener
記得看Google的api,上面都有。官方推薦的獲取hsp的代理。學習
再說一下:
A2DP,AVRCP。
找一個能看懂的解釋:
A2DP(高級音頻傳送規格)– 容許傳輸立體聲音頻信號。 (相比用於 HSP 和 HFP 的單聲道加密,質量要好得多)。
AVRCP(音頻/視頻遙控規格)–用於從控制器(如立體聲耳機)向目標設備(如裝有 Media Player 的電腦)發送命令(如前跳、暫停和播放)。google
而實際開發中,Android工程師要控制的就是A2DP的鏈接了。邏輯和獲取HSP的大體差很少。
構造函數:加密
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothAdapter.getProfileProxy(XApplication.getInstance(), new profileListener(), BluetoothProfile.A2DP); 獲取A2DP的代理: public class profileListener implements BluetoothProfile.ServiceListener { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { Log.i(TAG, "connect to a2dp server"); a2dpProfile = proxy; } @Override public void onServiceDisconnected(int profile) { Log.i(TAG, "disconnect to a2dp server"); a2dpProfile = null; } } 判斷A2DP的鏈接狀態: public boolean getA2dpState(BluetoothDevice device){ if(a2dpProfile != null && a2dpProfile.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED){ return true; } return false; }
A2DP與設備相連:
public void a2dpConnect(BluetoothDevice device){ if(a2dpProfile != null){ BluetoothA2dp a2dp = (BluetoothA2dp) a2dpProfile; Class<? extends BluetoothA2dp> clazz = a2dp.getClass(); Method m2; try { Log.i(TAG,"use reflect to connect a2dp"); m2 = clazz.getMethod("connect",BluetoothDevice.class); m2.invoke(a2dp, device); } catch (Exception e) { Log.e(TAG,"error:" + e.toString()); } } }
在實際開發中:關於A2DP,手機支持A2DP的(安卓機都是標配了),只須要經過廣播就能夠獲取狀態了。我實際在開發中,建議借鑑網絡鏈接的邏輯,一旦A2DP中斷,藍牙音響設備天然就沒有聲音了。因此,必須是全局的。
關於AVRCP,這部分嵌入式工程師的邏輯多,硬件上的按鍵能夠控制手機app的,好比按鍵加減時,能夠與app交互。
關於HSP,HFP。儘管能夠實現手機來電時,硬件設備也會響,天然接聽時,會被別人聽到,因此接聽時,須要嵌入式工程師作額外處理。Android工程師看狀況要不要配合一下。
仍是那句老話,強烈建議學習Google 官方在github的demo:https://github.com/googlesamples/
而後再根據本身的實際狀況代碼的擴展。
這裏回答一下評論裏面feer921的一個問題:如何判斷 其餘藍牙設備實現了哪些Profile呢?便是如何判斷某設備是媒體音頻、電話音頻的呢?
根據你獲取的藍牙設備。假設你拿到了爲:device。
獲取它的設備對象:
final int deviceClass = device.getBluetoothClass().getDeviceClass();
而後:
final int deviceClassMasked = deviceClass & 0x1F00; if(deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES) { //耳機 } else if(deviceClass == BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE) { //麥克風 } else if(deviceClassMasked == BluetoothClass.Device.Major.COMPUTER) { //電腦 } else if(deviceClassMasked == BluetoothClass.Device.Major.PHONE) { //手機 } else if(deviceClassMasked == BluetoothClass.Device.Major.HEALTH) { //健康類設備 } else { //藍牙:好比藍牙音響。 }