1.有些手机无法搜索的设备名
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
//有些手机扫描不到设备名称,只有在scanRecord里才有这些名字信息 test假定是我们关键字,然后我们名称是固定长度就可以截取出来了
String tmpName=new String(scanRecord);
LOG.i(TAG, "scanRecord 里的值:"+tmpName );
if(tmpName.contains("test")){
try {
String deviceName=tmpName.substring(tmpName.indexOf("test"),tmpName.indexOf("test")+13);
}catch (Exception e){
e.printStackTrace();
}
}
}
2. 6.0以上的蓝牙手机需要开启定位可以搜索到设备
权限ACCESS_COARSE_LOCATION
3.手机蓝牙在打开的时候可能是在自己搜索蓝牙设备,导致我们第一次去搜索蓝牙设备都搜不到,这个我时候我采用
首先如果蓝牙没打开,就先打开蓝牙
延迟两秒再执行
mBluetoothAdapter.stopLeScan(callback);
mBluetoothAdapter.startLeScan(callback);
4. vivo X20A还必须得有
android.permission.BLUETOOTH_PRIVILEGED
5.0以上系统静默开启蓝牙需要这个操作,不然会出现E/BluetoothAdapter: startLeScan: cannot get BluetoothLeScanner
5.华为荣耀3c 手机 蓝牙连接 出现过一次 搜索不到蓝牙设备,厂商提供搜索工具也搜索不到,其他手机打开蓝牙可以搜索到该设备,打开蓝牙开发检测后就可以了,再关闭蓝牙检测也可以,再也无法复现了
6.打开蓝牙三种方式
隐形打开(在5.0以上需要支持android.permission.BLUETOOTH_PRIVILEGED 有些手机比如Vivo拒绝后可能一直打开不了)
而且大部分手机如果隐式打开,还是会提示,所以还不如干脆直接显式打开,兼容性会更好
BluetoothManager mBluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothAdapter.enable();
显性打开(最好是在UI里有做提示用户来打开,这样对用户和兼容性比较好,太多自定义系统在权限那块兼容不太好)
//会弹出系统对话框,提示用户是否允许蓝牙设备对其他设备的可见性
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);//可被发现的持续时间
startActivity(intent);
//会弹出一个对话框,提示用户是否允许打开蓝牙
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
//打开系统蓝牙界面
Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
startActivity(intent);
7.蓝牙通讯联调
每次底层发送数据如果超过20个字节,或者是每20个字节发送间隔少于40ms(预估时间,如果还是容易蓝牙死掉,那么时间就需要延长)可能导致蓝牙死掉,Android收不到或者蓝牙设备的嵌入式死掉
8.蓝牙联调工具
https://download.csdn.net/download/z157794218/11777902 想改积分,但是改不了,也可以自行百度
Android是nRF connect.apk IOS是lightBlue
9.关于设置蓝牙mtu 大小
1)首先最大可设置成512(Android5.1上才支持)
2)我们使用indications方式,需要先enable的CCCD特征值
protected final boolean enableIndications(final BluetoothGattCharacteristic characteristic) {
final BluetoothGatt gatt = mBluetoothGatt;
if (gatt == null || characteristic == null)
return false;
// Check characteristic property
final int properties = characteristic.getProperties();
if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == 0)
return false;
boolean isEnableSuccess= gatt.setCharacteristicNotification(characteristic, true);
final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(mConfig.getUuidCccd()));
if (descriptor != null) {
boolean isSetValueSuccess=descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
return gatt.writeDescriptor(descriptor);
}
return false;
}
3)然后间隔500ms再调用修改mtu大小
public static void exchangeGattMtu(BluetoothGatt gatt,int mtu) {
int retry = 5;
boolean status = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
while ((false == status) && retry > 0) {
status = gatt.requestMtu(mtu);//这个是设置 我尝试过如果只设置一次,可能会设置不成功没有回调
retry--;
}
}
}
4)蓝牙服务设置成功后会回调
BluetoothGattCallback里onMtuChanged方法,告知实际上设置成功mtu的大小,你发送最大只能用这个去发送,超过也会发送不过去
5)关于MTU有个最坑的,坑了我几天的问题,下面重点(不要无缝操作蓝牙,一定需要有间隔时间)
在调用改变mtu大小前,请先将蓝牙读的特征值CCCD最好是enable(即2步骤),否则可能导致设置也无效,并且在读特征值enable和改变mtu大小之间一定要间隔500ms左右,不间隔看到设置成功也发送不了和接收不了数据
enableIndications(characteristic);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
exchangeGattMtu(gatt,512);
6)参考https://blog.csdn.net/make_progress/article/details/86551379
a.蓝牙MTU默认23个Byte(20个用于通讯,3个标志位)
core spec中ATT的默认MTU为23个Byte,ATT的Opcode占1个Byte、ATT的Handle2个Byte、GATT占20个Byte。
23Byte(ATT)=1Byte(Opcode)+2Byte(Handler)+20Byte(BATT)
b.不同的蓝牙版本最大MTU不同,例如:蓝牙4.2的最大MTU=247Byte(不一定正确,也有说是257Byte、也有说是241Byte),蓝牙5.0的最大MTU=512,有效的最大MTU还需要减去协议Byte、Opcode和Handler。蓝牙4.2的有效的最大MTU是244Byte(不一定正确),有兴趣的可以看看Bluetooth SIG官网https://bluetoothworldevent.com/welcome。
蓝牙4.2:1Byte(Opcode)+2Byte(Handler)+244Byte(BATT)=247Byte(不一定正确);
蓝牙5.0:没查到
蓝牙4.1和4.0是不支持大包模式,最好还是按23个mtu处理
10.ENABLE_NOTIFICATION_VALUE 这种方式类似网络中udp,丢过去就好了,不管成功与否
ENABLE_INDICATION_VALUE 这种方式类似网络的tcp(丢过去就会有回应ACK)
两者区别只是通过设置读特征值的CCCD客户端配置描述设置,因为其实底层都是广播的socket没有实际区别,只是系统蓝牙在上面做了一下所谓的ack的区别
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);