安卓蓝牙开发(1)BLE蓝牙基础知识和一般开发流程
一、BLE(Bluetooth Low Energy)低功耗蓝牙基本知识Android 4.3(API级别18)引入了以低功耗蓝牙(BLE)为中心角色的内置平台支持,并提供应用程序可用于发现设备,查询服务和传输信息的API常见用例包括以下内容:在附近的设备之间传输少量的数据与Google Beacons等接近传感器进行互动,为用户提供基于当前位置的定制体验。传统蓝牙可以用于数据量比较大的传输,如语
一、BLE(Bluetooth Low Energy)低功耗蓝牙基本知识
Android 4.3(API级别18)引入了以低功耗蓝牙(BLE)为中心角色的内置平台支持,并提供应用程序可用于发现设备,查询服务和传输信息的API
常见用例包括以下内容:
- 在附近的设备之间传输少量的数据
- 与Google Beacons等接近传感器进行互动,为用户提供基于当前位置的定制体验。
传统蓝牙可以用于数据量比较大的传输,如语音,音乐,较高数据量传输等,但是比较耗电,低功耗蓝牙这样应用于实时性要求比较高,功耗比较低,但是数据速率比较低的产品,如遥控类的,如鼠标,键盘,遥控鼠标(Air Mouse),传感设备的数据发送,如心跳带,血压计,温度传感器等。
1. GATT 和 ATT
GATT协议
(1)通用属性配置文件 Generic Attribute Profile(GATT):GATT配置文件是通过BLE链接发送和接收被称为“属性”的短小数据的通用规范。目前所有的低能耗应用程序都基于GATT。
- 蓝牙SIG为低能耗设备定义了许多配置文件。 配置文件是设备如何在特定应用程序中工作的规范。 请注意,设备可以实现多个配置文件。 例如,一个设备可以包含一个心率监测器和一个电池电量检测器。
-
可以把他看成xml来理解:
- 每个GATT由完成不同功能的Service组成;
- 每个Service由不同的Characteristic组成;
-
每个Characteristic由一个value和一个或者多个Descriptor组成;
Service、Characteristic相当于标签(Service相当于他的类别,Characteristic相当于它的名字),而value才真正的包含数据,Descriptor是对这个value进行的说明和描述,当然我们可以从不同角度来描述和说明,因此可以有多个Descriptor.
1. Service 的UUID: 设备中每一个不同的 Service 都有一个 128 bit 的 UUID 作为这个 Service 的独立标志。蓝牙核心规范制定了两种不同的UUID,一种是基本的UUID,一种是代替基本UUID的16位UUID。所有的蓝牙技术联盟定义UUID共用了一个基本的UUID:0x0000xxxx-0000-1000-8000-00805F9B34FB
2. 每一个 Characteristic 也有一个唯一的 UUID 作为标识符
举一个简单的例子进行说明:
常见的小米手环是一个BLE设备,(假设)它包含三个Service,分别是提供设备信息的Service、提供步数的Service、检测心率的Service;
设备信息的service中包含的characteristic包括厂商信息、硬件信息、版本信息等;
心率Service则包括心率characteristic等,心率characteristic中的value则真正的包含心率的数据,而descriptor则是对该value的描述说明,比如value的单位啊,描述啊,权限啊等。
(2)属性协议 Attribute Protocol(ATT):GATT建立在属性协议(ATT)之上。 这也被称为GATT / ATT。 ATT经过优化,可在BLE设备上运行。 为此,它使用尽可能少的字节。 每个属性由一个通用唯一标识符(UUID)唯一标识,该标识符是用于唯一标识信息的字符串ID的标准化128位格式。 ATT传输的属性被格式化为特征和服务。
- 特性 Characteristic: 特性包含描述特性值的单个值和0-n个描述符。一个特征可以被认为是一个类,类似于一个阶级,是最小的数据逻辑单元。
- 描述符 Descriptor: 描述符是描述特征值的定义属性。 例如,一个描述符可以指定一个可读的描述,一个特征值的可接受范围,或者一个特征值特有的度量单位。value、descriptor中存储数据的解析由Server的工程师决定,并无规范,双发按照约定开发。
- 服务 Service: 服务是一个特征的集合。 例如,您可以拥有一个名为“心率监测器”的服务,其中包含“心率测量”等特性。 您可以在bluetooth.org上找到现有基于GATT的配置文件和服务的列表。Service/Characteristic均有一个唯一的UUID标识,UUID既有16位的也有128位的,我们需要了解的是16位的UUID是经过蓝牙组织认证的,是需要购买的,当然也有一些通用的16位UUID。例如Heart Rate服务的UUID就是0X180D,代码中表示为0X00001800-0000-1000-8000-00805f9b34fb,其他位为固定的。而128位的UUID则可以自定义。
在 Android 开发中,建立蓝牙连接后,我们说的通过蓝牙发送数据给外围设备就是往这些 Characteristic 中的 Value 字段写入数据;外围设备发送数据给手机就是监听这些 Charateristic 中的 Value 字段有没有变化,如果发生了变化,手机的 BLE API 就会收到一个监听的回调。
2. 角色(Roles)和责任(Responsibilities)
Android设备与BLE设备交互时适用的角色和职责:
- 中央(Central) 与 周边(Peripheral)。这适用于BLE连接本身。处于中心角色的设备扫描,寻找广告,并且在外围角色中的设备进行广告。
- GATT服务器 与 GATT客户端。这决定了两台设备在建立连接后如何相互通话。
为了理解这个区别,假设你有一个Android手机和一个BLE设备的活动追踪器。 手机支持中心角色; 活动跟踪器支持外设角色(建立一个BLE连接,你需要每两个事物中只有一个支持外围设备的人不能彼此交谈,也不能只支持两个事物)。
一旦手机和活动追踪器建立了连接,他们就开始将GATT元数据转移到另一个。根据他们传输的数据的种类,其中一个或另一个可能充当服务器。例如,如果活动跟踪器想要将传感器数据报告给电话,则活动跟踪器可以充当服务器。如果活动跟踪器想要从手机接收更新,那么手机作为服务器可能是有意义的。
在本文档中使用的示例中,Android应用程序(在Android设备上运行)是GATT客户端。该应用程序从GATT服务器获取数据,GATT服务器是支持心率档案的BLE心率监测器。但你也可以设计你的Android应用程序来扮演GATT服务器的角色。
二、安卓BLE开发流程
首先要判断当前的Android设备是否支持蓝牙,如果支持则再判断当前蓝牙是否处于开启状态,如果未开启则发送广播通知系统开启蓝牙,蓝牙开启后开始搜索周围的蓝牙设备,注意搜索一定要设置超时处理,搜索到指定蓝牙设备后停止搜索任务。
此时可以以列表的形式供用户选择需要连接的设备,或者内部自动连接特定的设备,连接成功后,搜索此蓝牙设备提供的服务(特性、描述符的集合),搜索完成后设置一些对应的参数,即可与蓝牙设备进行通信了
1. 相关的API
我们在开发过程中需要用到的一些API:
- 1.BluetoothAdapter
本地蓝牙适配器,用于一些蓝牙的基本操作,比如判断蓝牙是否开启、搜索蓝牙设备等。
- 2.BluetoothDevice
蓝牙设备对象,包含一些蓝牙设备的属性,比如设备名称、mac地址等。
- 3.BluetoothProfile
一个通用的蓝牙规范,设备之间按照这个规范来收发数据。
- 4.BluetoothGatt
蓝牙通用属性协议,定义了BLE通讯的基本规则,是BluetoothProfile的实现类,Gatt是Generic Attribute Profile的缩写,用于连接设备、搜索服务等操作。
- 5.BluetoothGattCallback
蓝牙设备连接成功后,用于回调一些操作的结果,必须连接成功后才会回调。
- 6.BluetoothGattService
蓝牙设备提供的服务,是蓝牙设备特征的集合。
- 7.BluetoothGattCharacteristic
蓝牙设备特征,是构建GATT服务的基本数据单元。
- 8.BluetoothGattDescriptor
蓝牙设备特征描述符,是对特征的额外描述。
我们可以将上述几个类的关系比喻为:BluetoothDevice为学校,BluetoothGatt为学校到达某一个班级的通道,BluetoothCattService为学校的某一个班级,BluetoothCattCharacteristic为班级中的某一个学生。那么蓝牙连接通信的过程就是这样,BluetoothAdapter先找到学校(就是连接目的设备),再通过通道找到目标班级,最后从班级中找到目标学生,这个学生就是我们设备之间通信的中介,很重要,学校有唯一的MAC地址,班级有唯一的serviceUUID,学生有唯一的charactersticUUID(相当于学号),所以就是在一所学校找一个学生的问题。
2. 操作流程
(1)打开所需要的权限
在 Android 6.0 及以上,还需要打开位置权限。如果应用没有位置权限,蓝牙扫描功能不可用,即扫描不到BLE设备,但其它蓝牙操作例如连接蓝牙设备和写入数据不受影响。
<!-- 通用蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- 位置权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
(2)获取 BluetoothAdapter
BluetoothAdapter是任何和所有的蓝牙活动所必需的。 BluetoothAdapter代表设备自己的蓝牙适配器(蓝牙无线电)。整个系统有一个蓝牙适配器,您的应用程序可以使用这个对象与它进行交互。下面的代码展示了如何获取适配器。请注意,此方法使用getSystemService()返回 BluetoothManager 的实例,然后用于获取适配器。 Android 4.3(API Level 18)介绍了BluetoothManager:
private BluetoothAdapter mBluetoothAdapter;
...
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
(3)启动蓝牙
调用isEnabled()来检查当前是否启用了蓝牙。如果此方法返回false,则蓝牙被禁用。
以下片段检查是否启用了蓝牙。如果不是,该片段会显示一个错误,提示用户转到设置以启用蓝牙:
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
注意:
传递给 startActivityForResult(android.content.Intent,int)的REQUEST_ENABLE_BT常量是系统在onActivityResult(int,int,android.content)中返回给您的本地定义的整数(它必须大于0))实现作为requestCode参数。
(4)查找蓝牙设备
要查找BLE设备,请使用startLeScan()方法。 此方法将BluetoothAdapter.LeScanCallback作为参数。 您必须实现此BluetoothAdapter.LeScanCallback,因为这是如何返回扫描结果。 由于扫描耗电量大,您应遵守以下准则:
- 一旦找到所需的设备,请停止扫描
- 切勿扫描循环,并在扫描上设置时间限制。以前可用的设备可能已移出范围,并继续扫描电池电量。
以下片段显示了如何启动和停止扫描:
/**
* Activity for scanning and displaying available BLE devices.
*/
public class DeviceScanActivity extends ListActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
...
}
如果只想扫描特定类型的外设,则改为调用startLeScan(UUID [],BluetoothAdapter.LeScanCallback),提供指定您的应用程序支持的GATT服务的UUID对象数组。
以下是BluetoothAdapter.LeScanCallback的一个实现,它是用于传递BLE扫描结果的接口:
private LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
(5)读取设备BLE信息
一旦你的Android应用程序连接到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);
}
...
}
...
}
(6)接受GATT通知
BLE应用程序在设备上发生特定特征变化时要求收到通知是很常见的。这段代码展示了如何使用setCharacteristicNotification()方法为特性设置通知
private BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
boolean enabled;
...
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
...
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
一旦为特征启用了通知,如果特性在远程设备上发生变化,则会触发onCharacteristicChanged()回调:
@Override
// Characteristic notification
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
(7)关闭蓝牙
一旦您的应用程序使用BLE设备,应该调用close(),以便系统可以正确释放资源:
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
项目推荐:
Java微服务实战296集大型视频-谷粒商城【附代码和课件】
Java开发微服务畅购商城实战【全357集大项目】-附代码和课件
更多推荐
所有评论(0)