传输 BLE 数据

连接到 BLE GATT 后, 服务器,您可以使用 以了解设备上提供哪些服务、查询数据 ,并在特定 GATT 特征时请求通知 更改。

发现服务

在 BLE 设备上连接到 GATT 服务器后,首先要做的是 来执行服务发现。此信息可提供有关您所用服务 以及服务特征及其 描述符。在以下示例中,服务成功连接到 设备(通过对 onConnectionStateChange()BluetoothGattCallback), 该 discoverServices() 函数从 BLE 设备中查询信息。

该服务需要覆盖 onServicesDiscovered() 函数 BluetoothGattCallback。 此函数在设备报告其可用服务时被调用。

Kotlin

 class BluetoothLeService : Service() {  ...  private val bluetoothGattCallback = object : BluetoothGattCallback() {     override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {         if (newState == BluetoothProfile.STATE_CONNECTED) {             // successfully connected to the GATT Server             broadcastUpdate(ACTION_GATT_CONNECTED)             connectionState = STATE_CONNECTED             // Attempts to discover services after successful connection.             bluetoothGatt?.discoverServices()         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {             // disconnected from the GATT Server             broadcastUpdate(ACTION_GATT_DISCONNECTED)             connectionState = STATE_DISCONNECTED         }     }      override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {         if (status == BluetoothGatt.GATT_SUCCESS) {             broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED)         } else {             Log.w(BluetoothLeService.TAG, "onServicesDiscovered received: $status")         }     } }  ...  companion object {   const val ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"   const val ACTION_GATT_DISCONNECTED =               "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"   const val ACTION_GATT_SERVICES_DISCOVERED =               "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"    private const val STATE_DISCONNECTED = 0   private const val STATE_CONNECTED = 2 } 

Java

 class BluetoothLeService extends Service {      public final static String ACTION_GATT_SERVICES_DISCOVERED =             "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";      ...      private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {         @Override         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {             if (newState == BluetoothProfile.STATE_CONNECTED) {                 // successfully connected to the GATT Server                 connectionState = STATE_CONNECTED;                 broadcastUpdate(ACTION_GATT_CONNECTED);                 // Attempts to discover services after successful connection.                 bluetoothGatt.discoverServices();             } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {                 // disconnected from the GATT Server                 connectionState = STATE_DISCONNECTED;                 broadcastUpdate(ACTION_GATT_DISCONNECTED);             }         }          @Override         public void onServicesDiscovered(BluetoothGatt gatt, int status) {             if (status == BluetoothGatt.GATT_SUCCESS) {                 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);             } else {                 Log.w(TAG, "onServicesDiscovered received: " + status);             }         }     }; } 

该服务使用广播来通知 活动。发现服务后,服务便可调用 getServices()至 获取报告的数据。

Kotlin

 class BluetoothLeService : Service() {  ...    fun getSupportedGattServices(): List<BluetoothGattService?>? {       return bluetoothGatt?.services   } } 

Java

 class BluetoothLeService extends Service {  ...      public List<BluetoothGattService> getSupportedGattServices() {         if (bluetoothGatt == null) return null;         return bluetoothGatt.getServices();     } } 

然后,activity 在收到广播 intent 时可以调用此函数, 表示服务发现已完成。

Kotlin

 class DeviceControlActivity : AppCompatActivity() {  ...      private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() {         override fun onReceive(context: Context, intent: Intent) {             when (intent.action) {                 BluetoothLeService.ACTION_GATT_CONNECTED -> {                     connected = true                     updateConnectionState(R.string.connected)                 }                 BluetoothLeService.ACTION_GATT_DISCONNECTED -> {                     connected = false                     updateConnectionState(R.string.disconnected)                         }                 BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED -> {                     // Show all the supported services and characteristics on the user interface.                     displayGattServices(bluetoothService?.getSupportedGattServices())                 }             }         }     } } 

Java

 class DeviceControlsActivity extends AppCompatActivity {  ...      private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {         @Override         public void onReceive(Context context, Intent intent) {             final String action = intent.getAction();             if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {                 connected = true;                 updateConnectionState(R.string.connected);             } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {                 connected = false;                 updateConnectionState(R.string.disconnected);             } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {                 // Show all the supported services and characteristics on the user interface.                 displayGattServices(bluetoothService.getSupportedGattServices());             }         }     }; } 

读取 BLE 特性

一旦您的应用连接到 GATT 服务器并发现服务, 可以读取和写入属性(在支持的情况下)。例如,以下 该代码段会循环访问服务器的服务和特征,并显示 在界面中执行以下操作:

Kotlin

 class DeviceControlActivity : Activity() {      // Demonstrates how to iterate through the supported GATT     // Services/Characteristics.     // In this sample, we populate the data structure that is bound to the     // ExpandableListView on the UI.     private fun displayGattServices(gattServices: List<BluetoothGattService>?) {         if (gattServices == null) return         var uuid: String?         val unknownServiceString: String = resources.getString(R.string.unknown_service)         val unknownCharaString: String = resources.getString(R.string.unknown_characteristic)         val gattServiceData: MutableList<HashMap<String, String>> = mutableListOf()         val gattCharacteristicData: MutableList<ArrayList<HashMap<String, String>>> =                 mutableListOf()         mGattCharacteristics = mutableListOf()          // Loops through available GATT Services.         gattServices.forEach { gattService ->             val currentServiceData = HashMap<String, String>()             uuid = gattService.uuid.toString()             currentServiceData[LIST_NAME] = SampleGattAttributes.lookup(uuid, unknownServiceString)             currentServiceData[LIST_UUID] = uuid             gattServiceData += currentServiceData              val gattCharacteristicGroupData: ArrayList<HashMap<String, String>> = arrayListOf()             val gattCharacteristics = gattService.characteristics             val charas: MutableList<BluetoothGattCharacteristic> = mutableListOf()              // Loops through available Characteristics.             gattCharacteristics.forEach { gattCharacteristic ->                 charas += gattCharacteristic                 val currentCharaData: HashMap<String, String> = hashMapOf()                 uuid = gattCharacteristic.uuid.toString()                 currentCharaData[LIST_NAME] = SampleGattAttributes.lookup(uuid, unknownCharaString)                 currentCharaData[LIST_UUID] = uuid                 gattCharacteristicGroupData += currentCharaData             }             mGattCharacteristics += charas             gattCharacteristicData += gattCharacteristicGroupData         }     } } 

Java

 public class DeviceControlActivity extends Activity {     ...     // Demonstrates how to iterate through the supported GATT     // Services/Characteristics.     // In this sample, we populate the data structure that is bound to the     // ExpandableListView on the UI.     private void displayGattServices(List<BluetoothGattService> gattServices) {         if (gattServices == null) return;         String uuid = null;         String unknownServiceString = getResources().                 getString(R.string.unknown_service);         String unknownCharaString = getResources().                 getString(R.string.unknown_characteristic);         ArrayList<HashMap<String, String>> gattServiceData =                 new ArrayList<HashMap<String, String>>();         ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData                 = new ArrayList<ArrayList<HashMap<String, String>>>();         mGattCharacteristics =                 new ArrayList<ArrayList<BluetoothGattCharacteristic>>();          // Loops through available GATT Services.         for (BluetoothGattService gattService : gattServices) {             HashMap<String, String> currentServiceData =                     new HashMap<String, String>();             uuid = gattService.getUuid().toString();             currentServiceData.put(                     LIST_NAME, SampleGattAttributes.                             lookup(uuid, unknownServiceString));             currentServiceData.put(LIST_UUID, uuid);             gattServiceData.add(currentServiceData);              ArrayList<HashMap<String, String>> gattCharacteristicGroupData =                     new ArrayList<HashMap<String, String>>();             List<BluetoothGattCharacteristic> gattCharacteristics =                     gattService.getCharacteristics();             ArrayList<BluetoothGattCharacteristic> charas =                     new ArrayList<BluetoothGattCharacteristic>();            // Loops through available Characteristics.             for (BluetoothGattCharacteristic gattCharacteristic :                     gattCharacteristics) {                 charas.add(gattCharacteristic);                 HashMap<String, String> currentCharaData =                         new HashMap<String, String>();                 uuid = gattCharacteristic.getUuid().toString();                 currentCharaData.put(                         LIST_NAME, SampleGattAttributes.lookup(uuid,                                 unknownCharaString));                 currentCharaData.put(LIST_UUID, uuid);                 gattCharacteristicGroupData.add(currentCharaData);             }             mGattCharacteristics.add(charas);             gattCharacteristicData.add(gattCharacteristicGroupData);          }     ...     } ... } 

GATT 服务提供了一系列特征,您可以从 设备。要查询数据,请调用 readCharacteristic() 函数 BluetoothGatt,传入 BluetoothGattCharacteristic 想要阅读的内容

Kotlin

 class BluetoothLeService : Service() {  ...      fun readCharacteristic(characteristic: BluetoothGattCharacteristic) {         bluetoothGatt?.let { gatt ->             gatt.readCharacteristic(characteristic)         } ?: run {             Log.w(TAG, "BluetoothGatt not initialized")             Return         }     } } 

Java

 class BluetoothLeService extends Service {  ...      public void readCharacteristic(BluetoothGattCharacteristic characteristic) {         if (bluetoothGatt == null) {             Log.w(TAG, "BluetoothGatt not initialized");             return;         }         bluetoothGatt.readCharacteristic(characteristic);     } } 

在此示例中,服务实现了一个用于调用 readCharacteristic()。 这是一个异步调用。系统会将结果发送到 BluetoothGattCallback 函数 onCharacteristicRead()

Kotlin

 class BluetoothLeService : Service() {  ...      private val bluetoothGattCallback = object : BluetoothGattCallback() {          ...          override fun onCharacteristicRead(             gatt: BluetoothGatt,             characteristic: BluetoothGattCharacteristic,             status: Int             ) {                 if (status == BluetoothGatt.GATT_SUCCESS) {                 broadcastUpdate(BluetoothLeService.ACTION_DATA_AVAILABLE, characteristic)             }         }     } } 

Java

 class BluetoothLeService extends Service {  ...      private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {      ...          @Override         public void onCharacteristicRead(         BluetoothGatt gatt,         BluetoothGattCharacteristic characteristic,         int status         ) {             if (status == BluetoothGatt.GATT_SUCCESS) {                 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);             }         }     }; } 

当特定回调被触发时,它会调用相应的 broadcastUpdate() 辅助方法,并向其传递操作。请注意, 本部分根据蓝牙心率执行解析 测量配置文件规范。

Kotlin

 private fun broadcastUpdate(action: String, characteristic: BluetoothGattCharacteristic) {     val intent = Intent(action)      // This is special handling for the Heart Rate Measurement profile. Data     // parsing is carried out as per profile specifications.     when (characteristic.uuid) {         UUID_HEART_RATE_MEASUREMENT -> {             val flag = characteristic.properties             val format = when (flag and 0x01) {                 0x01 -> {                     Log.d(TAG, "Heart rate format UINT16.")                     BluetoothGattCharacteristic.FORMAT_UINT16                 }                 else -> {                     Log.d(TAG, "Heart rate format UINT8.")                     BluetoothGattCharacteristic.FORMAT_UINT8                 }             }             val heartRate = characteristic.getIntValue(format, 1)             Log.d(TAG, String.format("Received heart rate: %d", heartRate))             intent.putExtra(EXTRA_DATA, (heartRate).toString())         }         else -> {             // For all other profiles, writes the data formatted in HEX.             val data: ByteArray? = characteristic.value             if (data?.isNotEmpty() == true) {                 val hexString: String = data.joinToString(separator = " ") {                     String.format("%02X", it)                 }                 intent.putExtra(EXTRA_DATA, "$data\n$hexString")             }         }     }     sendBroadcast(intent) } 

Java

 private void broadcastUpdate(final String action,                              final BluetoothGattCharacteristic characteristic) {     final Intent intent = new Intent(action);      // This is special handling for the Heart Rate Measurement profile. Data     // parsing is carried out as per profile specifications.     if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {         int flag = characteristic.getProperties();         int format = -1;         if ((flag & 0x01) != 0) {             format = BluetoothGattCharacteristic.FORMAT_UINT16;             Log.d(TAG, "Heart rate format UINT16.");         } else {             format = BluetoothGattCharacteristic.FORMAT_UINT8;             Log.d(TAG, "Heart rate format UINT8.");         }         final int heartRate = characteristic.getIntValue(format, 1);         Log.d(TAG, String.format("Received heart rate: %d", heartRate));         intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));     } else {         // For all other profiles, writes the data formatted in HEX.         final byte[] data = characteristic.getValue();         if (data != null && data.length > 0) {             final StringBuilder stringBuilder = new StringBuilder(data.length);             for(byte byteChar : data)                 stringBuilder.append(String.format("%02X ", byteChar));             intent.putExtra(EXTRA_DATA, new String(data) + "\n" +                     stringBuilder.toString());         }     }     sendBroadcast(intent); } 

接收 GATT 通知

当出现特定特征时,BLE 应用通常会要求接收通知 更改在以下示例中,服务将实现 函数来调用 setCharacteristicNotification() 方法:

Kotlin

 class BluetoothLeService : Service() {  ...      fun setCharacteristicNotification(     characteristic: BluetoothGattCharacteristic,     enabled: Boolean     ) {         bluetoothGatt?.let { gatt ->         gatt.setCharacteristicNotification(characteristic, enabled)          // This is specific to Heart Rate Measurement.         if (BluetoothLeService.UUID_HEART_RATE_MEASUREMENT == characteristic.uuid) {             val descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG))             descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE             gatt.writeDescriptor(descriptor)         }         } ?: run {             Log.w(BluetoothLeService.TAG, "BluetoothGatt not initialized")         }     } } 

Java

 class BluetoothLeService extends Service {  ...      public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,boolean enabled) {         if (bluetoothGatt == null) {             Log.w(TAG, "BluetoothGatt not initialized");             Return;         }         bluetoothGatt.setCharacteristicNotification(characteristic, enabled);          // This is specific to Heart Rate Measurement.         if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {             BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));             descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);             bluetoothGatt.writeDescriptor(descriptor);         }     } } 

为某个特征启用通知后, onCharacteristicChanged() 回调将触发回调函数:

Kotlin

 class BluetoothLeService : Service() {  ...      private val bluetoothGattCallback = object : BluetoothGattCallback() {         ...          override fun onCharacteristicChanged(         gatt: BluetoothGatt,         characteristic: BluetoothGattCharacteristic         ) {             broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic)         }     } } 

Java

 class BluetoothLeService extends Service {  ...      private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {     ...          @Override         public void onCharacteristicChanged(         BluetoothGatt gatt,         BluetoothGattCharacteristic characteristic         ) {             broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);         }     }; }