2013 年 7 月,Android API 18 版本引入了对低功耗蓝牙 (BTLE) 的支持。随着越来越多的设备进入市场,增加对BTLE设备的支持已成为许多人的首要任务。但是,对于新手来说,深入研究Android BTLE文档可能有点令人生畏。本教程将演练 BTLE 的基本概念,然后显示代码片段,以进一步说明与远程设备的通信。

BTLE概念

顾名思义,低功耗蓝牙旨在提供与经典蓝牙相似的通信范围,同时使用更少的功率。BTLE 设备将进入睡眠模式,并仅在连接尝试或事件时唤醒。因此,软件开发人员需要了解BTLE中的一些基本概念。了解这些我们在进行经典蓝牙编程时不关心的概念。

GATT 配置文件

所有 BTLE 设备都实现一个或多个配置文件。配置文件是一个高级定义,描述如何使用服务来启用应用程序。低能耗应用程序配置文件基于通用属性配置文件 (GATT)。这是通过低能量链路发送和接收短数据(称为属性)的一般规范。

客户

客户端是启动 GATT 命令并接受响应的设备。对于我们的示例,Android设备将充当客户端,因为这是一个典型的用例。但是,Android BTLE API确实允许Android设备充当服务器。

服务器

服务器是接收 GATT 命令或请求并返回响应的设备。例如,心率监测器、健康温度计以及位置和导航设备充当服务器。

特征

特征是在客户端和服务器之间传输的数据值。例如,除了心率测量之外,心率监测器还可以报告其当前电池电压、设备名称或序列号。

服务

服务是一组特征,它们一起运行以执行特定功能。许多设备实现设备信息服务。此服务由制造商名称、型号、序列号和固件版本等特征组成。

描述符

描述符提供有关特征的其他信息。例如,温度值特征可能具有其单位(摄氏或华氏度)或有效范围的指示,即传感器可以测量的上限和下限值。

服务、特征和描述符统称为属性,由 UUID(128 位数字)标识。在这 128 位中,您通常只关心下面突出显示的 16 位。这些数字由蓝牙特别兴趣小组 (SIG) 预定义。

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxx

GATT 操作

以下是可以使用这些概念执行的操作示例。这些都是客户端可用于发现有关服务器的信息的命令。

  • 发现所有主要服务的 UUID。例如,此操作可用于确定设备是否支持设备信息服务。
  • 发现给定服务的所有特征。例如,一些心率监测器还包括身体传感器的位置特征。
  • 读取和写入特定特征的描述符。最常用的描述符之一是客户端特征配置描述符。这允许客户端将通知设置为指示通知特定特征。如果客户端设置了"已启用通知"位,则每当信息可用时,服务器就会向客户端发送一个值。同样,设置"指示已启用"位也将使服务器能够在数据可用时发送通知,但指示模式还需要客户端的响应。
  • 读取和写入特征。以心率监测器为例,客户端将读取心率测量特征。或者,客户端可能会在升级远程设备的固件时写入特征。

让它适用于安卓

现在我们已经了解了BTLE的基础知识,让我们重点介绍一下使您的Android应用程序正常工作的重要步骤。回想一下,您必须使用支持API 18或更高版本的设备。

AndroidManifest.xml

首先在清单中声明以下权限。蓝牙权限允许您与设备连接,BLUETOOTH_ADMIN权限允许您发现设备。

<uses-permission android:name=“android.permission.BLUETOOTH” />
<uses-permission android:name=“android.permission.BLUETOOTH_ADMIN” />
<uses-feature android:name=“android.hardware.bluetooth_le”  android:required=“true” />

获取蓝牙适配器并启用

以下代码将获取该类,这是设备发现所必需的。此代码还会检查 Android 设备是否启用了蓝牙,如果当前已禁用蓝牙,则会请求用户启用蓝牙。请注意,此过程的这一部分与使用蓝牙经典设备连接时要执行的操作相同。但是,在此步骤之后,相似之处就停止了。BluetoothAdapter

BluetoothManager btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
	
BluetoothAdapter btAdapter = btManager.getAdapter();
if (btAdapter != null && !btAdapter.isEnabled()) {
	Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);	
	startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
}

设备发现

通常,与设备连接的第一步是设备发现。这是一个异步过程。因此,我们必须创建每次发现设备时都会调用的实现。BluetoothAdapter.LeScanCallback

private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
	@Override
	public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
		// your implementation here
	}
}

使用上述实现,我们可以通过以下方法调用启动和停止设备发现:

btAdapter.startLeScan(leScanCallback);
btAdapter.stopLeScan(leScanCallback);

请注意: 找到所需设备后停止扫描是明智的。如 Android 文档中所述,发现过程会严重消耗 Android 设备的电池电量。

创建蓝牙加特回调

现在您有了一个对象,下一步是启动连接过程。这需要类的实例。此类中有几个有用的方法,但下面的代码将仅突出显示一些必要的方法。BluetoothDeviceBluetoothGattCallback

private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {

	@Override
	public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
		// this will get called anytime you perform a read or write characteristic operation
	}

	@Override
	public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { 
		// this will get called when a device connects or disconnects
	}

	@Override
	public void onServicesDiscovered(final BluetoothGatt gatt, final int status) { 
		// this will get called after the client initiates a 			BluetoothGatt.discoverServices() call
	}
}

最后,使用以下调用来启动连接:

BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(context, false, btleGattCallback);

了解服务和特征

假设连接尝试成功,则将在参数设置为 的情况下调用回调。在此事件之后,可以启动发现服务。顾名思义,以下调用的目标是确定远程设备支持哪些服务。BluetoothGattCallback.onConnectionStateChange()newStateBluetoothProfile.STATE_CONNECTED

bluetoothGatt.discoverServices();

当设备响应时,您将收到回调。您必须先收到此回调,然后才能获得受支持服务的列表。获得设备支持的服务列表后,请使用以下代码获取该服务的特征。BluetoothGattCallback.onServicesDiscovered()

List<BluetoothGattService> services = bluetoothGatt.getServices();
for (BluetoothGattService service : services) {
	List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
}

配置通知的描述符

到目前为止,我们已经连接到该设备,发现了它支持的服务,并检索了每个服务的特征列表。现在我们有了基本信息,让我们做一些有用的事情。以心率监测器为例,我们希望监测器向我们发送用户心率的定期读数。回想一下,我们之前讨论过客户端特征配置描述符,它将在远程设备上启用通知。下面的代码演示了如何获取描述符,然后启用通知标志。

for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
	//find descriptor UUID that matches Client Characteristic Configuration (0x2902)
	// and then call setValue on that descriptor

	descriptor.setValue( BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
	bluetoothGatt.writeDescriptor(descriptor);
}

请注意对 UUID 值 0x2902 的引用。我们之前讨论了我们通常关心的UUID的16位。每个服务、特征和描述符都有一个与之关联的 UUID。有关预定义 UUID 的完整列表,请参阅蓝牙 SIG 分配号码网站。

接收通知

当心率监测器将心率测量结果发送到 Android 设备时,您将在 中收到回调。下面的代码演示如何检索该信息。BluetoothGattCallback.onCharacteristicChanged()

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
	//read the characteristic data
	byte[] data = characteristic.getValue();
}

最后一步

最后,要断开连接并关闭 GATT 客户端,请进行以下调用:

bluetoothGatt.disconnect();
bluetoothGatt.close();

提示和观察

希望本教程将帮助您开发自己的 BTLE 应用程序。如果这是你的目标,你应该知道更多的事情。(文档中未找到的项目)。

  • 在尝试连接之前停止设备发现。已经说过设备发现应该停止以防止电池耗尽。但是,在尝试连接之前停止设备发现为我提供了更好的连接结果。
  • 将所有 GATT 操作(连接尝试、设备发现、特征读取和写入)排入队列,然后一次执行一个。有证据表明,较低层的 GATT 操作没有排队,如果您自己不处理,某些操作将被丢弃。
  • 在应用关闭时,请尽一切努力断开设备连接并关闭 GATT 客户端(无论是由于用户操作、意外崩溃,还是操作系统由于系统资源短缺而删除了应用)。如果会话保持打开状态,则用户可能难以重新连接,直到之前的连接超时,或者在 Android 设备上循环蓝牙,或者设备重新启动。
  • Android 示例通常让开发人员完成设备发现过程以获取蓝牙设备对象。但是,在典型用例中,用户希望每次都连接到同一远程设备(即只需连接到您拥有的心率监测器)。初始设备发现后,保存设备的 MAC 地址。看。然后,下次用户想要连接时,请使用该地址构造蓝牙设备对象。看。这将绕过设备发现,从而创建更流畅的用户体验。BluetoothDevice.getAddress()BluetoothAdapter.getRemoteDevice()

您可能感兴趣的: