Android中如何实现蓝牙的配对与连接

Android中如何实现蓝牙的配对与连接

这段时间在项目中负责做蓝牙的设置模块,蓝牙这部分不算简单,自己先是花了一些时间看系统的蓝牙设置代码,感觉有点熟了才开动的,但期间还是踩了不少坑,有些问题网上也搜不到,现在做完了,有一些心得,记录下来。

1. AndroidManifest中添加蓝牙管理权限

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

2. 打开蓝牙

打开蓝牙有两种方式,发送蓝牙开启请求或代码后台自动打开蓝牙。

2.1 发送蓝牙开启请求

先判断BluetoothAdapter是不是为空,为空有可能是系统没有蓝牙模块,再判断蓝牙的状态是不是开启的,不是开启的就发送请求。

BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent)
}

请求开启蓝牙效果图

2.2 后台自动打开蓝牙
if (btAdapter != null && !btAdapter.isEnabled()) {
btAdapter.enable();
}

3. 获取已经绑定(配对)的设备

Set<BluetoothDevice> bondedDevices = btAdapter.getBondedDevices();  //返回已绑定的蓝牙设备集

BluetoothDevice有如下方法:
getBondState() //获取设备绑定状态,BOND_NONE(未绑定), BOND_BONDING(正在绑定), BOND_BONDED(已绑定)
getType() //获取蓝牙类型,DEVICE_TYPE_CLASSIC(普通类型BR/EDR), DEVICE_TYPE_LE(低功耗类型), DEVICE_TYPE_DUAL(双类型BR/EDR/LE), DEVICE_TYPE_UNKNOWN(未知)
getAddress() //蓝牙地址
getBluetoothClass() //获取蓝牙类别(BluetoothClass),如手机、电脑、耳机,注意与蓝牙类型的区别
getName() //蓝牙设备名字

BluetoothClass蓝牙类别说明:
目前有12个大类,定义在BluetoothClass.Device.Major下
BITMASK、MISC、COMPUTER、PHONE、NETWORKING、AUDIO_VIDEO、PERIPHERAL、IMAGING、WEARABLE、TOY、HEALTH、UNCATEGORIZED

每个大类有若干个小类别,定义在BluetoothClass.Device下
如: AUDIO_VIDEO_WEARABLE_HEADSET、AUDIO_VIDEO_HANDSFREE、AUDIO_VIDEO_RESERVED、AUDIO_VIDEO_MICROPHONE、AUDIO_VIDEO_LOUDSPEAKE

4. 监听蓝牙设备的变化

// 监听蓝牙设备的变化
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND); //发现新设备
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //绑定状态改变
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //开始扫描
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); //结束扫描
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); //连接状态改变
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //蓝牙开关状态改变
BluetoothReceiver receiver = new BluetoothReceiver();
mActivity.registerReceiver(receiver, filter);

...

private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch(intent.getAction()) {
case BluetoothAdapter.ACTION_FOUND:
//获取扫描到的设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//判断扫描到的设备是不是在已绑定设备列表中,在就跳过
for (BluetoothDevice d: bondedDevices) {
if (d.getAddress().equals(device.getAddress())) {
return;
}
}
mScannedDevices.add(device);
break;

case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
//开始扫描
break;

case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
//结束扫描
break;

case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
//绑定状态改变
break;

case BluetoothAdapter.ACTION_STATE_CHANGED:
//蓝牙开关状态改变
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_OFF:
break;
case BluetoothAdapter.STATE_ON:
break;
}
break;
}
}
}

5. 绑定蓝牙设备

绑定比较简单,调用 BluetoothDevice的createBond就一句代码,调用后就开始尝试绑定。

private boolean createBond(BluetoothDevice device) {
return device.createBond();
}

绑定成功后会触发刚注册的蓝牙监听器,action为 ACTION_BOND_STATE_CHANGED

6. 取消绑定

BluetoothDevice绑定的方法是开放的,取消绑定的方法却是隐藏的,只对系统app开放,坑爹,只能用反射来解决了。

private boolean removeBond(BluetoothDevice device) {
Class btDeviceCls = BluetoothDevice.class;
Method removeBond = btDeviceCls.getMethod("removeBond");
removeBond.setAccessible(true);
return (boolean) removeBond.invoke(device);
}

7. 连接

绑定(配对)和连接是两个不同的过程,绑定是指两个设备发现了对方的存在,可以获取到对方的名称、地址等信息,有能力建立起连接;连接是指两个设备共享了一个RFCOMM通道,有能力进行数据互传。确认绑定上了之后,才能开始连接。可以试试蓝牙音箱的连接过程,就是先点击一次,开始配对,配对成功后出现在已绑定的列表中,再点击一次,就开始连接,连接成功后蓝牙音箱就有声音了。 
这一步我吃了不少亏,网上搜到的都是下面的这种连接方式

UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private void connect(BluetoothDevice device) {
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
socket.connect();
...

socket.close();
}

这种连接是用蓝牙来进行Socket通信的,而我要做的是如何把手机上连上蓝牙耳机或蓝牙音箱,这种方式是不行的。 
折腾了好久,最后找到了下面的连接方法,专门针对AUDIO、VIDEO类型的蓝牙设备的连接。

 if (device.getBluetoothClass().getMajorDeviceClass() != BluetoothClass.Device.Major.AUDIO_VIDEO) {
return;
}
btdapter.getProfileProxy(context, new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
BluetoothHeadset bluetoothHeadset = (BluetoothHeadset) proxy;
Class btHeadsetCls = BluetoothHeadset.class;
try {
Method connect = btHeadsetCls.getMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(bluetoothHeadset, device);
} catch (Exception e) {
Log.e(TAG, e + "");
}
}

@Override
public void onServiceDisconnected(int profile) {

}
}, BluetoothProfile.HEADSET);
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页