Android蓝牙BLE基本用法

Android蓝牙BLE基本用法

  • Android应用权限
  • 蓝牙相关对象获取
  • 检查设备是否支持BLE
  • 开启设备的蓝牙功能
  • 使设备的蓝牙可被发现
  • 开启BLE服务端
    • 新建一个GATT服务
    • 新建一个GATT特征值
    • 新建一个特征值描述(可选)
    • 特征值加入特征值描述(可选)
    • 服务加入特征值
    • 开启GATT服务端
    • GATT服务端加入刚才创建的GATT Service
    • 开始发送BLE广播
  • BLE客户端
    • 扫描设备和服务
    • 扫描回调接口
    • 客户端连接
    • 读操作
    • 写操作

Android应用权限






蓝牙相关对象获取

    private BluetoothAdapter bluetoothAdapter;
    private BluetoothManager bluetoothManager;

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // bluetoothAdapter
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        
        // bluetoothManager BLE需要这个类
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        }
    }

检查设备是否支持BLE

public void checkBLESupport() {
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "不支持BLE", Toast.LENGTH_LONG).show();
            Log.d("kaikai", "不支持BLE");

        } else {
            Toast.makeText(this, "支持BLE", Toast.LENGTH_LONG).show();
            Log.d("kaikai", "支持BLE");
        }
    }

开启设备的蓝牙功能

public void onEnableBlueTooth() {
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, 0);
        } else {
            Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_LONG).show();
        }
    }

使设备的蓝牙可被发现

public void onDiscoverable() {
        Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        startActivity(discoverableIntent);
        }

开启BLE服务端

新建一个GATT服务

BluetoothGattService service = new BluetoothGattService(YOUR_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);

新建一个GATT特征值

BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(YOUR_CHARACTERISTIC_UUID,
// 注意,如果要使特征值可写,PROPERTY需设置PROPERTY_WRITE(服务端需要返回响应)或PROPERTY_WRITE_NO_RESPONSE(服务端无需返回响应),
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);

注意,BluetoothGattCharacteristic构造函数的第二个参数,如果要使特征值可被客户端进行反写,需要有BluetoothGattCharacteristic.PROPERTY_WRITE或PROPERTY_WRITE_NO_RESPONSE标志位,其中,PROPERTY_WRITE标志位要求服务端在接收写请求时,返回响应,而PROPERTY_WRITE_NO_RESPONSE则不需要响应,但数据可能会被截断。

新建一个特征值描述(可选)

BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(YOUR_DESCRIPTER_UUID, BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);

特征值加入特征值描述(可选)

characteristic.addDescriptor(descriptor);

服务加入特征值

service.addCharacteristic(characteristic);

开启GATT服务端

        // 使用一个类成员保存BluetoothGattServer引用
        private BluetoothGattServer bluetoothGattServer;


        // onCreate方法中或某个按钮触发的回调函数中
        bluetoothGattServer = bluetoothManager.openGattServer(this, new BluetoothGattServerCallback() {
                @Override
                public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
                    // 连接状态改变
                    if (newState == BluetoothProfile.STATE_CONNECTED) {
                        Log.d("kaikai", "BLE服务端:BLE Connected!");
                    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                        Log.d("kaikai", "BLE服务端:BLE Disconnected!");
                    }
                }

                @Override
                public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {

                    Log.d("kaikai", "BLE服务端:接收到特征值读请求!" + " requestId=" + requestId + " offset=" + offset);

                    BluetoothGattCharacteristic gattCharacteristic = bluetoothGattServer.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);
                    if (characteristic == gattCharacteristic) {
                        // 证明characteristic与通过gattServer得到的是同一个对象
                        Log.d("kaikai", "BLE服务端:same characteristic!");
                    }

                    byte[] value = characteristic.getValue();
                    if (value == null) {
                        value = "init responseData".getBytes();
                        characteristic.setValue(value);
                    }

                    // offset != 0,如果读的偏移不为0,证明是分段读,返回特征值的对应便宜的截断数据就行(每次返回的长度都至末尾)
                    if (offset != 0) {
                        int newLen = value.length - offset;
                        byte[] retVal = new byte[value.length - offset];
                        System.arraycopy(value, offset, retVal, 0, newLen);
                        value = retVal;
                    }

                    // 请求读特征
                    if (TIME_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
                        bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
                    }
                }

                @Override
                public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
                    // super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);

                    Log.d("kaikai", "BLE Server端 onCharacteristicWriteRequest"
                            + " requestId:" + requestId + " offset:" + offset + " prepareWrite:" + preparedWrite
                            + " responseNeeded:" + responseNeeded + " value:" + new String(value));

                    // 不是分段写
                    if (!preparedWrite) {
                        characteristic.setValue(value);
                    }
                    // 分段写
                    else {
                        if (offset == 0) {
                            bleDataByteArray = new ByteArrayOutputStream();
                        }
                        try {
                            bleDataByteArray.write(value);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        writingObj = characteristic;
                    }

                    // 尝试用更改后的数据作为响应,发现客户端的回调的状态值为133,不为0
                    byte[] valueWrong = new byte[value.length];
                    System.arraycopy(value, 0, valueWrong, 0, value.length);
                    valueWrong[0]++;

                    if (responseNeeded) {
                        // 注意,如果写特征值需要响应(特征值的属性是PROPERTY_WRITE不是PROPERTY_WRITE_NO_RESPONSE),必需发送value作为响应数据
                        bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
                        // 此处为使用错误的数据做响应
//                      bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, valueWrong);
                    }
                }

                @Override
                public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
                    super.onExecuteWrite(device, requestId, execute);

                    // 当分段写时,才会回调此方法

                    Log.d("kaikai", "onExecuteWrite called! requestId=" + requestId + " execute=" + execute);

                    if (execute) {
                        byte[] data = bleDataByteArray.toByteArray();
                        String dataStr = new String(data);
                        Log.d("kaikai", "onExecuteWrite 拼接数据:" + dataStr);

//                        BluetoothGattCharacteristic characteristic1 = bluetoothGattServer.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);
//                        characteristic1.setValue(data);
                        if (writingObj != null) {
                            if (writingObj instanceof BluetoothGattCharacteristic) {
                                ((BluetoothGattCharacteristic) writingObj).setValue(data);
                            } else if (writingObj instanceof BluetoothGattDescriptor) {
                                ((BluetoothGattDescriptor) writingObj).setValue(data);
                            } else {
                                throw new RuntimeException("writingObj类型不明");
                            }
                        } else {
                            throw new RuntimeException("writingObj为空");
                        }

                        // 注意,当写数据过长时,会自动分片,多次调用完onCharacteristicWriteRequest后,便会调用此方法,要在此方法中发送响应,execute参数指示是否执行成功,可按照此参数发送响应的状态
                        bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
                    } else {
                        Log.d("kaikai", "BLE SERVER: onExecuteWrite发送失败响应");
                        // 发送一个失败响应
                        bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null);
                    }
                }

                @Override
                public void onServiceAdded(int status, BluetoothGattService service) {
                    super.onServiceAdded(status, service);

                    Log.d("kaikai", "onServiceAdded");
                }

                @Override
                public void onMtuChanged(BluetoothDevice device, int mtu) {
                    super.onMtuChanged(device, mtu);
                    Log.d("kaikai", "BLE SERVER:onMTUChanged! mtu=" + mtu);


//                    bluetoothGattServer.sendResponse(device,0,0,0,null);
                }

                @Override
                public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
                    super.onDescriptorReadRequest(device, requestId, offset, descriptor);

                    Log.d("kaikai", "BLE服务端:onDescriptorReadRequest  requestId=" + requestId + "  offset=" + offset);

                    byte[] value = descriptor.getValue();
                    if (value == null) {
                        value = "descriptor1 value".getBytes();
                    }

                    // 与characteristic的处理一样
                    if (offset > 0) {
                        int newLen = value.length - offset;
                        byte[] newValue = new byte[newLen];
                        System.arraycopy(value, offset, newValue, 0, newLen);
                        value = newValue;
                    }

                    bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
                }

                @Override
                public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
                    super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);

                    Log.d("kaikai", "BLE服务端:onDescriptorWriteRequest  requestId=" + requestId + "  preparedWrite=" + preparedWrite + "  responseNeeded=" + responseNeeded
                            + "  value=" + new String(value));

                    if (!preparedWrite) {
                        descriptor.setValue(value);
                    } else {
                        if (offset == 0) {
                            bleDataByteArray = new ByteArrayOutputStream();
                        }
                        try {
                            bleDataByteArray.write(value);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        writingObj = descriptor;
                    }

                    if (responseNeeded) {
                        bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
                    }

                }
            });

有几点需要注意:

  1. Characteristic和Descriptor的读和写,在交互数据过长(超出MTU)的情况下,都会产生分段(会回调多次相关的方法,并传入对应的offset值)。写操作主要判断prepareWrite参数,如果为true,则是分段写,并且在最后会回调onExecuteWrite方法。读操作主要用offset判断,只需返回Characteristic或Descriptor的value的在offset处的截断即可,详看代码解释。
  2. 可用一个临时成员保存正在分段写的Characteristic或Descriptor引用,上述代码使用了一个Object writingObj。
  3. 写操作的responseNeeded取决于Characteristic的构造函数的第二个参数。在responseNeeded==true时,服务端才需返回响应,并且返回的数据需要和传入的参数value相同。

GATT服务端加入刚才创建的GATT Service

bluetoothGattServer.addService(service);

开始发送BLE广播

    /**
     * 通知服务开启(发广告)
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void startAdvertising() {
        BluetoothLeAdvertiser advertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
        if (advertiser == null) {
            Log.d("kaikai", "advertiser为空");
            return;
        }

        AdvertiseSettings settings = new AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
                .setConnectable(true)
                .setTimeout(0)
                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
                .build();

        AdvertiseData data = new AdvertiseData.Builder().setIncludeDeviceName(true)
                .setIncludeTxPowerLevel(false)
                .addServiceUuid(new ParcelUuid(TIME_SERVICE_UUID))
                .build();

        advertiser.startAdvertising(settings, data, new AdvertiseCallback() {
            @Override
            public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                Log.d("kaikai", "BLE Advertise started!");
            }

            @Override
            public void onStartFailure(int errorCode) {

                Log.d("kaikai", "BLE Advertise fail!errorCode=" + errorCode);
            }
        });
    }

此处有几点需要注意:

  1. 如果设备名称过长,可能会导致广播失败
  2. 如果没有开启应用的定位权限,也可能会导致广播失败,请检查应用的相关权限有没开启

AdvertiseCallback回调接口用于判断是否成功开启BLE广播

至此,BLE服务端成功开启。

BLE客户端

扫描设备和服务

public void onBLEScan(View view) {
        bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);
    }

扫描回调接口

        scanCallback = new ScanCallback() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                super.onScanResult(callbackType, result);

                BluetoothDevice device = result.getDevice();

                Log.d("kaikai", "BLE发现设备:" + device.getName() + " " + device.getAddress());

                deviceAdapter.add("设备名:" + device.getName() + "地址:" + device.getAddress());
				
				// 这里我添加了自己特定的处理逻辑,大家按回自己的逻辑进行处理
                if (device.getName() != null && device.getName().equalsIgnoreCase("kk")) {
                    // 搜索到设备名为 “kk”,即可停止搜索了
                    Log.d("kaikai", "match!");
                    bluetoothAdapter.getBluetoothLeScanner().stopScan(scanCallback);

                    // 这里可接着写连接BluetoothGATT的逻辑,下面会介绍
                }

            }

            @Override
            public void onBatchScanResults(List results) {
                super.onBatchScanResults(results);

                Log.d("kaikai", "BatchScanResult" + results.toString());
            }

            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
                Log.d("kaikai", "BLE Scan failed!");
            }
        };

客户端连接

当搜索到你想要的设备对象(BluetoothDevice)时,就可进行连接了,使用BluetoothGATT对象进行

 // 连接
 blueToothGatt = device.connectGatt(MainActivity.this, false, new BluetoothGattCallback() {
     @Override
     public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
         super.onConnectionStateChange(gatt, status, newState);

         if (newState == BluetoothProfile.STATE_CONNECTED) {
             Log.d("kaikai", "BLE客户端:BLE连接成功");
             blueToothGatt.discoverServices();
             if (blueToothGatt == gatt) {
                 Log.d("kaikai", "BLE客户端:same gatt object!");
             }
             Log.d("kaikai", "BLE客户端:开始搜索外围服务");
         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
             Log.d("kaikai", "BLE客户端:断开BLE连接");
         }
     }

     @Override
     public void onServicesDiscovered(BluetoothGatt gatt, int status) {
         super.onServicesDiscovered(gatt, status);

         if (status == BluetoothGatt.GATT_SUCCESS) {
             Log.d("kaikai", "BLE客户端:成功搜索到服务");

             List services = gatt.getServices();

             BluetoothGattService service = gatt.getService(TIME_SERVICE_UUID);

             if (service == null) {
                 Log.d("kaikai", "service为空");
                 return;
             }

             // 对应的特征
             BluetoothGattCharacteristic characteristic = service.getCharacteristic(TIME_CHARACTERISTIC_UUID);
             gatt.setCharacteristicNotification(characteristic, true);


             boolean b;

             // 更改特性描述

             // 读一下特性
//                                b = gatt.readCharacteristic(characteristic);
//                                Log.d("kaikai", "b:" + b);

             // 如果调用了readCharacteristic,则睡眠数秒后再调用readDescriptor也会返回false,应该是同一线程内不能与服务端交互多次
//                                try {
//                                    Thread.sleep(5000);
//                                } catch (InterruptedException e) {
//                                    e.printStackTrace();
//                                }

             // 注意,前面如果调用了readCharacteristic,这里就不能直接进行descriptor读取,会返回false。如果没有调用则可
             BluetoothGattDescriptor descriptor = characteristic.getDescriptor(TIME_DESCRIPTER_UUID);
             b = gatt.readDescriptor(descriptor);
             Log.d("kaikai", "descriptor b:" + b);

         }
     }

     @Override
     public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
         super.onCharacteristicRead(gatt, characteristic, status);

         if (status == BluetoothGatt.GATT_SUCCESS) {
             Log.d("kaikai", "BLE客户端:成功读取到特性");
             byte[] value = characteristic.getValue();
             try {
                 String val = new String(value, "utf8");
                 Log.d("kaikai", val);
             } catch (UnsupportedEncodingException e) {
                 e.printStackTrace();
             }

             // 注意,descriptor的读取须要在完成characteristic读取后执行
//                                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(TIME_DESCRIPTER_UUID);
//                                boolean b = gatt.readDescriptor(descriptor);
//                                Log.d("kaikai", "b2:" + b);
         }
     }

     @Override
     public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
         super.onCharacteristicWrite(gatt, characteristic, status);

         Log.d("kaikai", "BLE客户端:onCharacteristicWrite called!status=" + status);

         if (status == BluetoothGatt.GATT_SUCCESS) {
             Log.d("kaikai", "BLE客户端:写特征值成功");
             byte[] value = characteristic.getValue();
             Log.d("kaikai", "value:" + new String(value));
         }
     }

     @Override
     public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
         super.onCharacteristicChanged(gatt, characteristic);

         Log.d("kaikai", "BLE客户端:onCharacteristicChanged");
         byte[] newValue = characteristic.getValue();
         try {
             Log.d("kaikai", new String(newValue, "utf8"));
         } catch (UnsupportedEncodingException e) {
             e.printStackTrace();
         }
     }

     @Override
     public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
         super.onDescriptorRead(gatt, descriptor, status);

         Log.d("kaikai", "BLE客户端:onDescriptorRead  status=" + status);
         Log.d("kaikai", "BLE客户端:成功读取descriptor:" + new String(descriptor.getValue()));
     }

     @Override
     public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
         super.onDescriptorWrite(gatt, descriptor, status);

         Log.d("kaikai", "BLE客户端:onDescriptorWrite  status=" + status);
     }

     @Override
     public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
         super.onMtuChanged(gatt, mtu, status);

         Log.d("kaikai", "BLE客户端: onMTUChanged. mtu=" + mtu + " status=" + status);

         if (status != BluetoothGatt.GATT_SUCCESS) {
             Log.d("kaikai", "BLE客户端: onMTUChanged status不为0");
         }
     }
 });

客户端不需要处理分段问题。
onCharacteristicChanged方法可监听服务端推送的特征值改变请求

读操作

    /**
     * BLE客户端读取特征值1
     *
     * @param view
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void onBLEClientReadCharacteristic1(View view) {
        BluetoothGattCharacteristic characteristic = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);
        blueToothGatt.readCharacteristic(characteristic);
    }

   /**
     * BLE客户端读取descriptor
     *
     * @param view
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void onClientReadDescriptor(View view) {

        BluetoothGattDescriptor descriptor = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID).getDescriptor(TIME_DESCRIPTER_UUID);

        boolean readDescriptorBool = blueToothGatt.readDescriptor(descriptor);

        Log.d("kaikai", "readDescriptorBool:" + readDescriptorBool);

    }

读取成功后,会回调BluetoothGattCallback的相关方法

写操作

    /**
     * BLE客户端写特征值1
     *
     * @param view
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void onBLEClientWriteCharacteristic1(View view) throws UnsupportedEncodingException {
        BluetoothGattCharacteristic characteristic = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);

        byte[] characteristicData = characteristic.getValue();
        if (characteristicData != null) {
            Log.d("kaikai", "BLE客户端写特征值前的特征值为:" + new String(characteristicData, "utf8"));
        } else {
            Log.d("kaikai", "characteristic value为空");
        }
        characteristic.setValue("client write charrrrrrrrrrrrrr 9999999994444".getBytes());
        blueToothGatt.writeCharacteristic(characteristic);
    }
    /**
     * BLE客户端读写descriptor
     *
     * @param view
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public void onClientWriteDescriptor(View view) {
        byte[] dataToWrite = "1111111111222222222233333333334444444444".getBytes();

        BluetoothGattDescriptor descriptor = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID)
                .getDescriptor(TIME_DESCRIPTER_UUID);

        descriptor.setValue(dataToWrite);
        boolean result = blueToothGatt.writeDescriptor(descriptor);
        Log.d("kaikai", "descriptorWriteBool:" + result);
    }

写操作成功后,会回调BluetoothGattCallback的相关方法

你可能感兴趣的:(Android)