The project needs to access two low-power Bluetooth devices (BLE) and interact (read/write) data with them. Therefore, I have read the official introduction of this part and summarized some points needing attention in BLE development and the basic process.

BLE development requires Android 4.3 (API Level 18) and above

To enable normal use of Bluetooth related functions (such as scanning), you need to add the following permissions:

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

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />Copy the code

On Android6.0 and above, we need to dynamically apply for permissions; RxPermissions is recommended

Here’s a quick look at how RxPermissions is introduced.

1. Add code to the root build file:

. allprojects { repositories { maven { url 'https://jitpack.io' } } } ...Copy the code

2. Add dependencies to the build file of the corresponding moudle:

. Dependencies {implementation 'com. Making. Tbruyelle: rxpermissions: 0.10.2'}...Copy the code

3. Use:

RxPermissions rxPermissions=new RxPermissions(this); rxPermissions.request(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest. Permission. ACCESS_FINE_LOCATION). The subscribe (granted - > {the if (granted) {/ / permissions allow successful}});Copy the code

To learn more about RxPermissions, click here.

There are two processing methods to determine whether the device supports Bluetooth:

  • If you want your app to only be installed on phones that support BLE, add the following to the manifest file so that your app won’t be installed on devices that don’t support BLE, which is not very friendly of course:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>Copy the code
  • Determine in the code whether the current device supports BLE to give feedback to the user.

    First, declare in the manifest file that you need to use the BLE feature, but set to false in the case of required, and pass it when the app runsPackageManager.hasSystemFeature()To determine whether the device supports BLE:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
Copy the code
// Use this check to determine whether BLE is supported on the device. Then // you can selectively disable BLE-related features. if (! getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); }Copy the code

BLE device scanning is implemented by BluetoothManager object provided methods, there are two scanning methods:

public boolean startLeScan(BluetoothAdapter.LeScanCallback callback) { throw new RuntimeException("Stub!" ); } public boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback) { throw new RuntimeException("Stub!" ); }Copy the code

The second method allows us to provide specific UUID, a specific device to scan, scan results by BluetoothAdapter. LeScanCallback callback interface to us:

 public interface LeScanCallback {
        /**
         * Callback reporting an LE device found during a device scan initiated
         * by the {@link BluetoothAdapter#startLeScan} function.
         *
         * @param device Identifies the remote device
         * @param rssi The RSSI value for the remote device as reported by the
         *             Bluetooth hardware. 0 if no RSSI value is available.
         * @param scanRecord The content of the advertisement record offered by
         *                   the remote device.
         */
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
    }Copy the code

After the device is scanned, we usually select a scanned device and obtain a remote Bluetooth device object through its address.

BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address)Copy the code

The first step in interacting with a BLE device is to connect to it, and more specifically, to the GATT service on the device. To connect to the GATT service on a BLE device, use the connectGatt () method. The method takes three parameters: a context object, autoConnect (a Boolean value indicating whether the BLE device is automatically connected to when it is available), and a reference to BluetoothGattCallback:

mBluetoothGatt = device.connectGatt(context, true, mGattCallback);Copy the code

The above code can connect to a GATT service hosted by a BLE device and return an instance of BluetoothGatt, which can then be used to perform GATT client operations such as writing data. The caller (Android application) is the GATT client. The connection status and data changes of the GATT are called back to the client (APP) through the BluetoothGattCallback interface.

These callback methods of BluetoothGattCallback are generally used:

1. Obtain the connection status and scan for device services when the connection is successful

@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { if (connectChangedListener ! = null) { connectChangedListener.onConnected(); } mConnectionState = STATE_CONNECTED; mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { if (connectChangedListener ! = null) { connectChangedListener.onDisconnected(); } mConnectionState = STATE_DISCONNECTED; }}Copy the code

2. Access to services, features such as a BLE BluetoothGattService equipment may have multiple services, each service can have multiple BluetoothGattCharacteristic same features.

@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { List<BluetoothGattService> services = mBluetoothGatt.getServices(); for (int i = 0; i < services.size(); i++) { HashMap<String, BluetoothGattCharacteristic> charMap = new HashMap<>(); BluetoothGattService bluetoothGattService = services.get(i); String serviceUuid = bluetoothGattService.getUuid().toString(); List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics(); for (int j = 0; j < characteristics.size(); j++) { charMap.put(characteristics.get(j).getUuid().toString(), characteristics.get(j)); } servicesMap.put(serviceUuid, charMap); } BluetoothGattCharacteristic bluetoothGattCharacteristic = getBluetoothGattCharacteristic(UUID_SERVICE, UUID_CHARACTERISTIC); if (bluetoothGattCharacteristic == null) return; enableGattServicesNotification(bluetoothGattCharacteristic); } else { Log.w(TAG, " --------- onServicesDiscovered received: " + status); }}Copy the code

In the code above, we will be BLE devices all the BluetoothGattService and BluetoothGattCharacteristic is preserved, but in the actual demand, We usually only with a feature of a particular BluetoothGattService BluetoothGattCharacteristic for data reading and writing. The judgment conditions are UUID_SERVICE and UUID_CHARACTERISTIC. These two UUID are generally provided to us when providing BLE devices.

After finding this particular BluetoothGattCharacteristic, we hope it can be notified when changes, you can use setCharacteristicNotification () method for feature set up notifications:

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(UUID_DESCRIPTOR));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);Copy the code

After the above Settings, the BLE data can be retrieved from the onCharacteristicChanged callback method:

@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {/ / analytical data parseData (characteristic); }Copy the code

Of course, we could also send data to the BLE device using the mBluetoothGatt obtained in step 5:

mBleGattCharacteristic.setValue(HexUtil.hexStringToBytes(value));
boolean b=mBluetoothGatt.writeCharacteristic(mBleGattCharacteristic);
Copy the code

The above is the basic development process of communication between Android terminal and BLE device. Here I have selected a Demo, and the project directory is as follows:

A few notes:

  • As my requirement here is to access two BLE devices, I extracted a BluetoothLeDeviceBase, which represents the base class device, and encapsulated some generic properties and operations there
  • Bluetoothledevice EA, bluetoothleDevice EB stands for a specific BLE device, each of which may have some differences, such as the way data is parsed-and so on.

Full code address: github.com/SolveBugs/B… At present, it is only basic packaging, and will continue to improve in the future.

Bluetoothbledemo this moudle can run, the interface is as follows: