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 { //蓝牙:好比蓝牙音响。 }