最近公司项目要通过手机蓝牙连接硬件设备,硬件设备的通讯方式采用的是BLE低功耗蓝牙,低功耗蓝牙与传统用蓝牙的区别现在不做赘述,BLE在Android4.3以上系统开始提供支持,且目前大部分的蓝牙通讯模式采用的都是低功耗蓝牙,下面是我再开发中了解到额一些知识,在这里做基础总结,如果有不对和欠妥的地方请指正。原谅小弟的学疏才浅
1、权限声明
特别注意蓝牙搜索需要位置权限
<!--声明蓝牙权限-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
1 获取蓝牙是适配器
蓝牙适配器 BluetoothAdapter在此可以看做是搜索蓝牙设备的工具获得方式:
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3开始搜索蓝牙设备
设计到一个搜索回调
BluetoothAdapter.LeScanCallback leScanCallback;
//蓝牙连接返回的对象
BluetoothGatt bluetoothGatt;
//搜索回调 leScanCallback = new BluetoothAdapter.LeScanCallback() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { //停止搜索到条件 比如我们搜索到名字叫xx的设备就停止搜索 if (!TextUtils.isEmpty(device.getName()) && "xx".equals(device.getName().trim())) { if (adapter.isDiscovering()) { adapter.stopLeScan(leScanCallback); } //蓝牙连接 bluetoothGatt = device.connectGatt(ConnectActivivty.this, false, bluetoothGattCallback); } } };
BluetoothGatt 是个非常重要的类,它由蓝牙连接成功后返回,连接成功的后得读写操作都要靠他来完成,所以一般把他定义成全局变量
我们连接获取BluetoothGatt 之后就要进行服务端连接,我在这里是这么认为的,蓝牙设备连接成功之后现在还不能进行通讯,BluetoothGatt 下包含很多服务也就是 BluetoothGattService,而每个BluetoothGattService下面又有很多蓝牙通道BluetoothGattCharacteristic,我们只有确定了蓝牙传输通道,在这个通道内才可以进行通讯。
上面的
bluetoothGattCallback就是设备连接时候要设置的回调
//连接的回调 bluetoothGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothProfile.STATE_CONNECTED) { //这里是子线程你不要做UI操作,否侧程序无法继续执行 //连接成功 //Toast.makeText(ConnectActivivty.this, "连接成功", Toast.LENGTH_LONG).show(); //去发现服务 bluetoothGatt.discoverServices(); } } //发现服务 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); //我们通过服务id来获取唯一服务,这个一般由硬件厂商指定,当然它也有一个默认值 BluetoothGattService bluetoothGattService = bluetoothGatt.getService(UUID.fromString("00001000-0000-1000-8000-00805f9b34fb")); //我们通过特征(也就是我们说的通道)id来获取唯一读取特征,这个一般由硬件厂商指定,当然它也有一个默认值 BluetoothGattCharacteristic characteristic = bluetoothGattService.getCharacteristic(UUID.fromString("00001002-0000-1000-8000-00805f9b34fb")); 拿到通道后我们设置监听此通道 gatt.setCharacteristicNotification(characteristic, true); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); Log.e("hhh", new String(characteristic.getValue())); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); } //特征值发生变化,我们认为有新的消息发来 @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); Log.e("hhh", new String(characteristic.getValue())); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); } @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); } };
以下是在真正进入蓝牙连接之前索要做的零基础判断,包括是否支持BLE,蓝牙和定位是否开启,权限是否开启
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { ToastUtils.showShort("您的手机不支持此功能"); return; } boolean isBlueToothEnable = isBlueToothEnable(); boolean isLocalEnable = isLocationEnabled(); if (!isBlueToothEnable && isLocalEnable) { ToastUtils.showShort("请打开蓝牙功能"); return; } if (isBlueToothEnable && !isLocalEnable) { ToastUtils.showShort("请打开定位功能"); return; } if (!isBlueToothEnable && !isLocalEnable) { ToastUtils.showShort("请打开蓝牙和定位功能"); return; } XXPermissions.with(ChooseCheckItemActivity.this) .constantRequest() .permission(new ArrayList(){{ add(Manifest.permission.ACCESS_COARSE_LOCATION); add(Manifest.permission.BLUETOOTH); add(Manifest.permission.ACCESS_FINE_LOCATION); add(Manifest.permission.BLUETOOTH_ADMIN);}}) .request(new OnPermission() { @Override public void hasPermission(List<String> granted, boolean isAll) { if (isAll) { if (ChooseCheckItemActivity.this != null) { //前往蓝牙连接 Intent intent = new Intent(ChooseCheckItemActivity.this, CheckBloodActivity.class); startActivity(intent); finish(); } ALog.dTag("", "获取权限成功"); } else { ALog.dTag("", "获取权限成功,部分权限未正常授予"); } } @Override public void noPermission(List<String> denied, boolean quick) { if (quick) { ALog.dTag("", "被永久拒绝授权,请手动授予权限"); //如果是被永久拒绝就跳转到应用权限系统设置页面 XXPermissions.gotoPermissionSettings(ChooseCheckItemActivity.this); } else { ALog.dTag("", "获取权限失败"); } } }); } });
//蓝牙是否打开 public boolean isBlueToothEnable() { BluetoothAdapter mBluetoothAdapter = BluetoothAdapter .getDefaultAdapter(); if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { return true; } return false; } //定位手机定位功能是否打开 public boolean isLocationEnabled() { int locationMode = 0; String locationProviders; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { locationMode = Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE); } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); return false; } return locationMode != Settings.Secure.LOCATION_MODE_OFF; } else { locationProviders = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED); return !TextUtils.isEmpty(locationProviders); } }