赞
踩
Android 4.3(API级别18)引入了以低功耗蓝牙(BLE)为中心角色的内置平台支持,并提供应用程序可用于发现设备,查询服务和传输信息的API
常见用例包括以下内容:
- 在附近的设备之间传输少量的数据
- 与Google Beacons等接近传感器进行互动,为用户提供基于当前位置的定制体验。
传统蓝牙可以用于数据量比较大的传输,如语音,音乐,较高数据量传输等,但是比较耗电,低功耗蓝牙这样应用于实时性要求比较高,功耗比较低,但是数据速率比较低的产品,如遥控类的,如鼠标,键盘,遥控鼠标(Air Mouse),传感设备的数据发送,如心跳带,血压计,温度传感器等。
GATT协议
(1)通用属性配置文件 Generic Attribute Profile(GATT):GATT配置文件是通过BLE链接发送和接收被称为“属性”的短小数据的通用规范。目前所有的低能耗应用程序都基于GATT。
可以把他看成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 就会收到一个监听的回调。
Android设备与BLE设备交互时适用的角色和职责:
为了理解这个区别,假设你有一个Android手机和一个BLE设备的活动追踪器。 手机支持中心角色; 活动跟踪器支持外设角色(建立一个BLE连接,你需要每两个事物中只有一个支持外围设备的人不能彼此交谈,也不能只支持两个事物)。
一旦手机和活动追踪器建立了连接,他们就开始将GATT元数据转移到另一个。根据他们传输的数据的种类,其中一个或另一个可能充当服务器。例如,如果活动跟踪器想要将传感器数据报告给电话,则活动跟踪器可以充当服务器。如果活动跟踪器想要从手机接收更新,那么手机作为服务器可能是有意义的。
在本文档中使用的示例中,Android应用程序(在Android设备上运行)是GATT客户端。该应用程序从GATT服务器获取数据,GATT服务器是支持心率档案的BLE心率监测器。但你也可以设计你的Android应用程序来扮演GATT服务器的角色。
首先要判断当前的Android设备是否支持蓝牙,如果支持则再判断当前蓝牙是否处于开启状态,如果未开启则发送广播通知系统开启蓝牙,蓝牙开启后开始搜索周围的蓝牙设备,注意搜索一定要设置超时处理,搜索到指定蓝牙设备后停止搜索任务。
此时可以以列表的形式供用户选择需要连接的设备,或者内部自动连接特定的设备,连接成功后,搜索此蓝牙设备提供的服务(特性、描述符的集合),搜索完成后设置一些对应的参数,即可与蓝牙设备进行通信了
我们在开发过程中需要用到的一些API:
本地蓝牙适配器,用于一些蓝牙的基本操作,比如判断蓝牙是否开启、搜索蓝牙设备等。
蓝牙设备对象,包含一些蓝牙设备的属性,比如设备名称、mac地址等。
一个通用的蓝牙规范,设备之间按照这个规范来收发数据。
蓝牙通用属性协议,定义了BLE通讯的基本规则,是BluetoothProfile的实现类,Gatt是Generic Attribute Profile的缩写,用于连接设备、搜索服务等操作。
蓝牙设备连接成功后,用于回调一些操作的结果,必须连接成功后才会回调。
蓝牙设备提供的服务,是蓝牙设备特征的集合。
蓝牙设备特征,是构建GATT服务的基本数据单元。
蓝牙设备特征描述符,是对特征的额外描述。
我们可以将上述几个类的关系比喻为:BluetoothDevice为学校,BluetoothGatt为学校到达某一个班级的通道,BluetoothCattService为学校的某一个班级,BluetoothCattCharacteristic为班级中的某一个学生。那么蓝牙连接通信的过程就是这样,BluetoothAdapter先找到学校(就是连接目的设备),再通过通道找到目标班级,最后从班级中找到目标学生,这个学生就是我们设备之间通信的中介,很重要,学校有唯一的MAC地址,班级有唯一的serviceUUID,学生有唯一的charactersticUUID(相当于学号),所以就是在一所学校找一个学生的问题。
(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集大项目】-附代码和课件
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。