Android藍牙開發中電話音頻(HSP,HFP)和媒體音頻(A2DP,AVRCP)究竟是個什麼鬼

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());
        }
    }
}

注意了:這裏運用到了Java反射的原理。


在實際開發中:關於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
    {
        //藍牙:好比藍牙音響。
    }
相關文章
相關標籤/搜索