赞
踩
Android 在API18/Android4.3版本开始为低功耗蓝牙 (BLE) 提供内置平台支持,并提供应用程序可用于发现设备、查询服务和传输信息的 API。
利用低功耗蓝牙可以实现:1.在附近的硬件设备之间传输少量数据。2.与接近传感器交互,为用户提供基于其当前位置的定制服务。
与经典蓝牙相比,BLE 旨在显着降低功耗。这允许应用程序与具有更严格电源要求的 BLE 设备进行通信,例如接近传感器、心率监测器和健身设备。
另外需要注意的是,当用户使用 BLE 将他们的设备与另一台设备配对时,用户设备上的所有应用程序都可以访问这两个设备之间通信的数据。 出于这个原因,如果应用程序捕获了敏感数据,应该实施应用程序层安全性以保护该数据的隐私。
支持BLE的设备要在彼此之间传输数据,它们必须首先形成一个通信通道。使用蓝牙 LE API 需要在清单文件中声明多个权限。一旦你的应用程序获得使用蓝牙的权限,您的**应用程序需要访问蓝牙适配器并确定设备上的蓝牙是否可用。**如果蓝牙可用,设备将扫描附近的 BLE 设备。找到设备后,通过连接到 BLE 设备上的 GATT 服务器来发现 BLE 设备的功能。建立连接后,可以根据可用服务和特征与连接的设备传输数据。 以下是需要添加的权限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- android M 以上版本获取周边蓝牙设备必须定位权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
GATT 配置文件是用于通过 BLE 链路发送和接收称为“属性”的短数据片段的通用规范。当前所有的 BLE 应用配置文件都基于 GATT。下面的网址是GitHub 上的 Android BluetoothLeGatt 示例。 https://github.com/android/connectivity-samples/tree/master/BluetoothLeGatt
蓝牙 SIG 为 BLE 设备定义了许多配置文件(Profiles)。配置文件是关于设备如何在特定应用程序中工作的规范。一台设备可以实现多个配置文件。例如,设备可能包含心率监测器和电池电量检测器。
GATT 建立在属性协议 (ATT) 之上。这也称为 GATT/ATT。 ATT 针对在 BLE 设备上运行进行了优化。为此,它使用尽可能少的字节。每个属性都由通用唯一标识符 (UUID) 唯一标识,它是用于唯一标识信息的字符串 ID 的标准化 128 位格式。 ATT 传输的属性被格式化为特征和服务。
特征包含单个值和描述特征值的 0-n 个描述符。一个特性可以被认为是一种类型,类似于一个类。描述符是描述特征值的已定义属性。例如,描述符可能指定人类可读的描述、特征值的可接受范围或特定于特征值的度量单位。
服务是特征的集合。例如,你可以拥有一项名为“心率监测器”的服务,其中包括“心率测量”等特征。你可以在 bluetooth.org 上找到现有的基于 GATT 的配置文件和服务的列表。
当设备与 BLE 设备交互时,适用以下角色和职责:
中央与外设。这适用于 BLE 连接本身。**中心角色的设备扫描,寻找广告,外围角色的设备制作广告。**在之后的示例代码中也有设计到广告的定义。
GATT 服务器与 GATT 客户端。这决定了两个设备在建立连接后如何相互通信。要区别它们的不同,假设有一部 Android 手机和一个 BLE 设备的活动跟踪器。电话支持中心角色;活动跟踪器支持外围角色。如果要建立 BLE 连接,您需要包含这两者,仅支持外围设备的两个设备无法相互通信,两个仅支持中央设备的设备也不能相互通信。
一旦手机和活动跟踪器建立连接,它们就会开始相互传输 GATT 元数据。根据他们传输的数据类型,其中一个可能充当服务器。例如,如果活动跟踪器想要向手机报告传感器数据,则活动跟踪器充当服务器可能是有意义的。如果活动跟踪器想要从手机接收更新,那么手机充当服务器可能是有意义的。
在本主题中使用的示例中,应用程序(在 Android 设备上运行)是 GATT 客户端。该应用程序从 GATT 服务器获取数据,该服务器是支持心率配置文件的 BLE 心率监视器。您也可以设计您的应用程序来扮演 GATT 服务器角色。
要查找 BLE 设备,请使用 startScan() 方法。此方法将 ScanCallback 作为参数。您必须实现此回调,因为这是返回扫描结果的方式。由于扫描会消耗大量电池电量,因此您应遵守以下准则:
找到所需设备后,立即停止扫描。
切勿循环扫描,并始终设置扫描时间限制。以前可用的设备可能已超出范围,继续扫描会耗尽电池电量。 在以下示例中,BLE 应用程序提供了一个活动 (DeviceScanActivity) 来扫描可用的蓝牙 LE 设备并将它们显示在一个列表中给用户。以下代码段显示了如何开始和停止扫描:
private BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner(); private boolean scanning; private Handler handler = new Handler(); // Stops scanning after 10 seconds. private static final long SCAN_PERIOD = 10000; private void scanLeDevice() { if (!scanning) { // Stops scanning after a predefined scan period. handler.postDelayed(new Runnable() { @Override public void run() { scanning = false; bluetoothLeScanner.stopScan(leScanCallback); } }, SCAN_PERIOD); scanning = true; bluetoothLeScanner.startScan(leScanCallback); } else { scanning = false; bluetoothLeScanner.stopScan(leScanCallback); } }
注意:BluetoothLeScanner 仅在设备当前启用蓝牙的情况下可从 BluetoothAdapter 获得。如果未启用蓝牙,则 getBluetoothLeScanner() 返回 null。
要仅扫描特定类型的外围设备,您可以改为调用 startScan(List, ScanSettings, ScanCallback),提供限制扫描查找的设备的 ScanFilter 对象列表和指定有关扫描的参数的 ScanSettings 对象. 以下代码示例是 ScanCallback 的实现,它是用于传递 BLE 扫描结果的接口。找到结果后,会将它们添加到 DeviceScanActivity 中的列表适配器以显示给用户。
private LeDeviceListAdapter leDeviceListAdapter = new LeDeviceListAdapter();
// Device scan callback.
private ScanCallback leScanCallback =
new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
leDeviceListAdapter.addDevice(result.getDevice());
leDeviceListAdapter.notifyDataSetChanged();
}
};
注意:您只能扫描蓝牙 LE 设备或扫描经典蓝牙设备,如蓝牙概述中所述。您不能同时扫描蓝牙 LE 和经典设备。
与 BLE 设备交互的第一步是连接到它。更具体地说,**连接到设备上的 GATT 服务器。**要连接到 BLE 设备上的 GATT 服务器,请使用 connectGatt() 方法。此方法采用三个参数:一个 Context 对象、autoConnect(一个布尔值,指示是否在 BLE 设备可用时立即自动连接),以及对 BluetoothGattCallback 的引用:
bluetoothGatt = device.connectGatt(this, false, gattCallback);
这将连接到由 BLE 设备托管的 GATT 服务器,并返回一个 BluetoothGatt 实例,然后您可以使用该实例来执行 GATT 客户端操作。调用者(Android 应用程序)是 GATT 客户端。 BluetoothGattCallback 用于将结果传递给客户端,例如连接状态,以及任何进一步的 GATT 客户端操作。
在以下示例中,BLE 应用程序提供了一个 Activity(DeviceControlActivity)来连接蓝牙设备,显示设备数据,并显示设备支持的 GATT 服务和特性。根据用户输入,此活动与名为 BluetoothLeService 的服务通信,该服务通过 BLE API 与 BLE 设备交互。使用绑定服务执行通信,该服务允许 Activity 连接到 BluetoothLeService 并调用函数以连接到设备。 BluetoothLeService 需要一个 Binder 实现来提供对活动服务的访问。
class BluetoothLeService extends Service { private Binder binder = new LocalBinder(); @Nullable @Override public IBinder onBind(Intent intent) { return binder; } class LocalBinder extends Binder { public BluetoothLeService getService() { return BluetoothLeService.this; } } }
Activity 可以使用 bindService() 启动服务,传入一个 Intent 来启动服务,一个 ServiceConnection 实现来监听连接和断开事件,以及一个标志来指定额外的连接选项。
class DeviceControlActivity extends AppCompatActivity { private BluetoothLeService bluetoothService; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { // 在服务上调用函数以检查连接并连接到设备 } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.gatt_services_characteristics); Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } }
服务绑定后,需要访问蓝牙适配器。它应该检查设备上的适配器是否可用。阅读https://developer.android.google.cn/guide/topics/connectivity/bluetooth/setup?hl=en以获取有关蓝牙适配器的更多信息。
下面的示例将此设置代码包装在一个 initialize() 函数中,该函数返回一个指示成功的布尔值。
class BluetoothLeService extends Service { public static final String TAG = "BluetoothLeService"; private BluetoothAdapter bluetoothAdapter; public boolean initialize() { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false; } return true; } ... }
活动在其 ServiceConnection 实现中调用此函数。处理来自 initialize() 函数的错误返回值取决于您的应用程序。您可以向用户显示一条错误消息,指示当前设备不支持蓝牙操作或禁用任何需要蓝牙才能工作的功能。在以下示例中,在 Activity 上调用 finish() 以将用户返回到上一个屏幕。
class DeviceControlsActivity extends AppCompatActivity { private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { if (!bluetoothService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } // 执行设备连接 } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } }; ... }
BluetoothService 初始化后,即可连接到 BLE 设备。活动需要将设备地址发送到服务,以便它可以启动连接。该服务将首先调用 BluetoothAdapter 上的 getRemoteDevice() 来访问设备。如果适配器无法找到具有该地址的设备,getRemoteDevice() 将引发 IllegalArgumentException。
public boolean connect(final String address) {
if (bluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
try {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
} catch (IllegalArgumentException exception) {
Log.w(TAG, "Device not found with provided address.");
return false;
}
// connect to the GATT server on the device
}
一旦服务被初始化,DeviceControlActivity 就会调用这个 connect() 函数。 Activity 需要传入 BLE 设备的地址。在以下示例中,设备地址作为额外的意图传递给活动。
private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { bluetoothService = ((LocalBinder) service).getService(); if (bluetoothService != null) { if (!bluetoothService.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } // 执行设备连接 bluetoothService.connect(deviceAddress); } } @Override public void onServiceDisconnected(ComponentName name) { bluetoothService = null; } };
一旦活动告诉服务连接到哪个设备并且服务连接到设备,服务需要连接到 BLE 设备上的 GATT 服务器。此连接需要 BluetoothGattCallback 来接收有关连接状态、服务发现、特征读取和特征通知的通知。 本主题重点介绍连接状态通知。
当与设备的 GATT 服务器的连接发生变化时,会触发 onConnectionStateChanged() 函数。在以下示例中,回调在 Service 类中定义,因此**一旦服务连接到 BluetoothDevice,它就可以与 BluetoothDevice 一起使用。**也便于服务使用BluetoothDevice来进行下一步的连接和使用。
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
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
}
}
};
一旦声明了 BluetoothGattCallback,服务就可以使用 connect() 函数中的 BluetoothDevice 对象连接到设备上的 GATT 服务。
使用了 connectGatt() 函数。这需要一个 Context 对象、一个 autoConnect 布尔标志和 BluetoothGattCallback。在此示例中,应用程序直接连接到 BLE 设备,因此为 autoConnect 传递 false。 还添加了 BluetoothGatt 属性。这允许服务在不再需要时关闭连接。
class BluetoothService extends Service { ... private BluetoothGatt bluetoothGatt; ... public boolean connect(final String address) { if (bluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } try { final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); // connect to the GATT server on the device bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback); return true; } catch (IllegalArgumentException exception) { Log.w(TAG, "Device not found with provided address. Unable to connect."); return false; } } }
当服务器与 GATT 服务器连接或断开连接时,它需要通知活动新状态。有几种方法可以做到这一点。以下示例使用广播将信息从服务发送到活动。
该服务声明了一个广播新状态的函数。这个函数接受一个动作字符串,该字符串在被广播到系统之前被传递给一个 Intent 对象。
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
一旦广播功能到位,它就会在 BluetoothGattCallback 中用于发送有关与 GATT 服务器的连接状态的信息。常量和服务的当前连接状态在表示 Intent 操作的服务中声明。
class BluetoothService extends Service { public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; private static final int STATE_DISCONNECTED = 0;p private static final int STATE_CONNECTED = 2; private int connectionState; ... 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); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server connectionState = STATE_DISCONNECTED; broadcastUpdate(ACTION_GATT_DISCONNECTED); } } }; … }
一旦服务广播连接更新,活动需要实现一个广播接收器。在设置活动时注册此接收器,并在活动离开屏幕时取消注册。通过监听来自服务的事件,Activity 能够根据与 BLE 设备的当前连接状态更新用户界面。
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); } } }; @Override protected void onResume() { super.onResume(); registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()); if (bluetoothService != null) { final boolean result = bluetoothService.connect(deviceAddress); Log.d(TAG, "Connect request result=" + result); } } @Override protected void onPause() { super.onPause(); unregisterReceiver(gattUpdateReceiver); } private static IntentFilter makeGattUpdateIntentFilter() { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); return intentFilter; } }
BroadcastReceiver 还用于传达服务发现以及来自设备的特征数据。
处理蓝牙连接的一个重要步骤是在完成连接后关闭连接。为此,请在 BluetoothGatt 对象上调用 close() 函数。在以下示例中,服务持有对 BluetoothGatt 的引用。当活动与服务解除绑定时,连接将关闭以避免耗尽设备电池。
class BluetoothService extends Service { @Override public boolean onUnbind(Intent intent) { close(); return super.onUnbind(intent); } private void close() { if (bluetoothGatt == null) { Return; } bluetoothGatt.close(); bluetoothGatt = null; } }
连接到 BLE GATT 服务器后,您可以使用该连接来了解设备上可用的服务、从设备查询数据以及在某个 GATT 特性发生变化时请求通知。
连接到 BLE 设备上的 GATT 服务器后,要做的第一件事就是执行服务发现。这提供了有关远程设备上可用服务以及服务特征及其描述符的信息。在以下示例中,一旦服务成功连接到设备(通过适当调用 BluetoothGattCallback 的 onConnectionStateChange() 函数指示),discoverServices() 函数会从 BLE 设备查询信息。 这个服务需要重写了 BluetoothGattCallback 中的 onServicesDiscovered() 函数。当设备报告其可用服务时会调用此函数。
class BluetoothService 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() 来获取报告的数据。就是将这些服务的数据放置到一个集合中。
class BluetoothService extends Service {
...
public List<BluetoothGattService> getSupportedGattServices() {
if (bluetoothGatt == null) return null;
return bluetoothGatt.getServices();
}
}
Activity 可以在收到广播意图时调用此函数,表明服务发现已完成。
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()); } } }; }
一旦您的应用程序连接到 GATT 服务器并发现服务,它就可以在支持的情况下读取和写入属性。例如,以下代码片段遍历服务器的服务和特征并将它们显示在 UI 中:
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 服务提供了可以从设备读取的特征列表。为了查询数据,调用 BluetoothGatt 上的 readCharacteristic() 函数,传入您要读取的 BluetoothGattCharacteristic。
class BluetoothService extends Service {
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (bluetoothGatt == null) {
Log.w(TAG, "BluetoothGatt not initialized");
return;
}
bluetoothGatt.readCharacteristic(characteristic);
}
}
在此示例中,服务实现了一个函数来调用 readCharacteristic()。这是一个异步调用。结果被发送到 BluetoothGattCallback 函数 onCharacteristicRead()。
class BluetoothService 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() 辅助方法并向其传递一个操作。请注意,本节中的数据解析是根据蓝牙心率测量配置文件规范执行的。
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); }
当设备上的特定特征发生变化时,BLE 应用程序通常会要求得到通知。在以下示例中,服务实现了一个函数来调用 setCharacteristicNotification() 方法:
class BluetoothService 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() 回调:
class BluetoothService extends Service {
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(
BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic
) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。