权限
<!--android 6-11-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--android 12-->
<!--蓝牙连接-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!--蓝牙扫描-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s"/>
android 12以上声明 android:usesPermissionFlags="neverForLocation"
后不需位置权限
扫描
private static void startScanLeIfEnabled() {
if(scanner==null) {
BluetoothManager manager = (BluetoothManager) Utils.getApp().getSystemService(BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = manager.getAdapter();
Log.i("testNet", "enable:" + mBluetoothAdapter.isEnabled());
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
// mBluetoothAdapter.startLeScan()
scanner = mBluetoothAdapter.getBluetoothLeScanner();
}else{
ToastUtils.showShort("请开启蓝牙");
}
}
scanner.startScan(scanCallback);
}
private static BluetoothLeScanner scanner;
使用 BluetoothScanner 调用startScan进行扫描,扫描会持续进行,直到调用 stopScan,回调结果在ScanCallback的 onScanResult中,每次返回一条,需做去重操作
连接
连接这里使用了第三方ViseBle,也可以自己实现
ViseBle.getInstance().connectByMac(bleMAC, connectCallback);
在回调中拿到 List<BluetoothGattService> gattServices = deviceMirror.getBluetoothGatt().getServices();
根据设备提供的服务码找到符合条件的BluetoothGattService
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
switch (gattCharacteristics.get(i).getProperties()){
case BluetoothGattCharacteristic.PROPERTY_READ://读取
propertyType=PropertyType.PROPERTY_READ;
break;
case BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE://写 无返回
// propertyType=PropertyType.PROPERTY_WRITE;
continue;
// break;
case BluetoothGattCharacteristic.PROPERTY_WRITE://写
propertyType=PropertyType.PROPERTY_WRITE;
break;
case BluetoothGattCharacteristic.PROPERTY_NOTIFY://通知
case BluetoothGattCharacteristic.FORMAT_UINT16:
propertyType=PropertyType.PROPERTY_NOTIFY;
break;
case BluetoothGattCharacteristic.PROPERTY_INDICATE://indicate 指令
propertyType=PropertyType.PROPERTY_INDICATE;
break;
}
从服务中拿到特征,根据特征的通道类型建立连接,读写等会有不同的回调,需要绑定多个渠道
bindChannel(bluetoothLeDevice,
propertyType,
serviceUUID,
characterUUID,
null);
public void bindChannel(BluetoothLeDevice bluetoothLeDevice, PropertyType propertyType, UUID serviceUUID,
UUID characteristicUUID, UUID descriptorUUID) {
DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice);
if (deviceMirror != null) {
Log.e(TAG, "serviceUUID:"+serviceUUID+" cUUID:"+characteristicUUID+" descriptorUUID:"+descriptorUUID+" properTyp:"+propertyType);
BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder()
.setBluetoothGatt(deviceMirror.getBluetoothGatt())
.setPropertyType(propertyType)
.setServiceUUID(serviceUUID)
.setCharacteristicUUID(characteristicUUID)
.setDescriptorUUID(descriptorUUID)
.builder();
deviceMirror.bindChannel(bleCallback, bluetoothGattChannel);
//if(propertyType==PropertyType.PROPERTY_INDICATE||propertyType==PropertyType.PROPERTY_NOTIFY) {
deviceMirror.registerNotify(propertyType == PropertyType.PROPERTY_INDICATE);
//}
// Log.e(TAG, "getGattInfoKey:" + bluetoothGattChannel.getGattInfoKey());
// deviceMirror.setNotifyListener(bluetoothGattChannel.getGattInfoKey(), receiveCallback);
}
}
消息发送
DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice);
if(deviceMirror!=null) {
deviceMirror.writeData(jsonString);
// deviceMirror.
}
消息接收
发出消息时建立消息的监听
DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice);
deviceMirror.setNotifyListener(bluetoothGattInfo.getGattInfoKey(), receiveCallback);
在回调中获取设备返回的数据
private IBleCallback receiveCallback = new IBleCallback() {
@Override
public void onSuccess(final byte[] data, BluetoothGattChannel bluetoothGattInfo, BluetoothLeDevice bluetoothLeDevice) {
if (data == null) {
return;
}
if(receiveListener!=null){
receiveListener.onSuccess(data,bluetoothGattInfo,bluetoothLeDevice);
}
Log.e(TAG, "处理数据:"+new String(data));
ToastUtils.showShort(new String(data));
//此时进行获取数据的处理
}
@Override
public void onFailure(BleException exception) {
if (exception == null) {
return;
}
Log.e(TAG, "数据接收失败:"+exception.toString());
}
};
状态判断
- 注册广播
BluetoothDevice.ACTION_BOND_STATE_CHANGED
,接收配对状态(只会在首次连接有回调,配对成功后,下次连接没有该回调)
BroadcastReceiver searchDevices=new BroadcastReceiver() {
@SuppressLint("MissingPermission")
@Override
public void onReceive(Context context, Intent intent) {
if(BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())){
BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.e("TAG", "onReceive:"+device.getBondState());
switch (device.getBondState()){
case BluetoothDevice.BOND_BONDING://正在配对
LogUtils.i("连接中");
break;
case BluetoothDevice.BOND_BONDED://配对结束
LogUtils.i("已配对");
break;
case BluetoothDevice.BOND_NONE:
LogUtils.i("已解除配对");
break;
}
}
}
};
2.同理注册广播BluetoothDevice.ACTION_ACL_CONNECTED
接收连接成功状态
3.由于连接状态是从 连接中—连接成功—配对成功 流转,当下次连接时不会走到配对成功的状态,连接成功即结束,所以要判断是否完成流程需在连接成功处判断是否已完成配对:
public static boolean checkPairDeviceContent(BluetoothDevice device) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> devices = adapter.getBondedDevices();
return devices!=null&&devices.contains(device);
}
特例
正常蓝牙连接使用GATT连接,在某些设备上会出现连接后系统层没有名称(应用层连上了,系统层没连上),连接配对出现多次配对弹窗等情况,可尝试使用反射建立配对,配对成功后再用gatt连接
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(mac);
if (device != null) {
if (!isBonding && device.getBondState() != BluetoothDevice.BOND_BONDED) {
try {
Method method = device.getClass().getMethod("createBond");
method.invoke(device);
isBonding = true;
} catch (Exception e) {
e.printStackTrace();
}
Log.e(TAG, "bluetoothGatt==配对中");
}else{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
bluetoothGatt = device.connectGatt(context, true, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
} else {
bluetoothGatt = device.connectGatt(context, true, bluetoothGattCallback);
}
}
}
借鉴
implementation 'com.vise.xiaoyaoyou:baseble:2.0.6'