翻译的官方文档,原文在这里:点击打开链接 ,本人对部分内容进行了微调,大意和原文一致。
蓝牙低功耗
Android 4.3(API级别18)为处于中心角色的低能耗蓝牙(BLE)引入了内置的平台支持,并为应用程序提供了可用于发现设备,查询服务和传输信息的API。
常见用例包括: 在附近设备之间传输少量数据。 与Google Beacons等距离传感器进行交互,可以根据用户的当前位置为用户提供个性化的体验。 与经典蓝牙相反,低功耗 蓝牙 (BLE)旨在提供显著的低功耗。 这将允许Android应用程序与具有更严格功率需求的BLE设备进行通信,例如距离传感器,心率监视器和健身设备。 关键术语和概念 以下是关键BLE术语和概念的摘要: 通用属性配置文件(GATT) - GATT配置文件是通过BLE链接发送和接收称为“属性”的短数据的通用规范。 所有当前的低能耗应用配置文件均基于GATT。 Bluetooth SIG定义了低能耗设备的许多配置文件。 配置文件是设备在特定应用程序中如何工作的规范。 请注意,设备可以实现多个配置文件。 例如,设备可以包含心率监测器和电池电平检测器。 属性协议(ATT)-GATT建立在属性协议(ATT)之上。 这也被称为GATT / ATT。 ATT经过优化,可在BLE设备上运行。 为此,它使用尽可能少的字节。 每个属性由通用唯一标识符(UUID)唯一标识,该标识符是用于唯一标识信息的字符串ID的标准化128位格式。 由ATT传输的属性被格式化为特征和服务。 特征 - 特征包含描述特征值的单个值和0-n描述符。 一个特征可以被认为是一个类型,类似于一个类。 描述符 - 描述符是描述特征值的定义属性。 例如,描述符可以指定人类可读描述,特征值的可接受范围,或特定于特征值的特定度量单位。 服务 - 服务是特征的集合。 例如,您可以使用一个名为“心率监测器”的服务,其中包含“心率测量”等特征。 您可以在bluetooth.org上找到现有的基于GATT的配置文件和服务列表。角色和责任 以下是Android设备与BLE设备进行交互的角色和职责: 中央与外围。 这适用于BLE连接本身。处于中央角色的设备扫描,寻找蓝牙设备,处于外围角色中的设备则成为可连接的设备。 GATT服务器与GATT客户端。 这确定了两个设备在建立连接之后如何对话。 为了解这个区别,现在假设你有一个Android手机和一个BLE设备的活动跟踪器。 手机支持中心角色; 活动跟踪器支持外设角色(要建立一个BLE连接,您需要做下面两件事情之一 ——两个只支持外围设备的东西不能互相交谈,也不能两个只支持中心设备)。 一旦手机和活动跟踪器建立了连接,他们就开始 相互传送 GATT元数据。 根据传输的数据类型,一个或另一个可能充当服务器。 例如,如果活动跟踪器想要将传感器数据报告给手机,那么活动跟踪器可以用作服务器。 如果活动跟踪器想要从手机接收更新,则手机充当服务器可能是有意义的。 在本文档的使用的示例中,Android应用(在Android设备上运行)是GATT客户端。 该应用程序从GATT服务器获取数据,GATT服务器是支持心率谱的BLE心率监测器。 但您也可以设置您的Android应用来充当GATT服务器的角色。 有关详细信息请参阅BluetoothGattServer。
如果您要声明您的应用仅适用于支持BLE的设备,请在应用的清单中包含以下内容:
1 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>但是,如果您希望将应用程序提供给不支持BLE的设备,则应将此元素包含在应用程序的清单中,但setrequired =“false”。 然后在运行时,您可以使用PackageManager.hasSystemFeature()来确定BLE的可用性:
1 // Use this check to determine whether BLE is supported on the device. Then 2 // you can selectively disable BLE-related features. 3 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) 4 { 5 Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); 6 finish(); 7 }为了从NETWORK_PROVIDER或GPS_PROVIDER接收位置更新,您必须在Android清单文件中声明{@code ACCESS_COARSE_LOCATION}或{@code ACCESS_FINE_LOCATION}权限来请求用户权限。 没有这些权限,您的应用程序对位置更新的请求将失败,并将显示权限错误。
如果您使用的是NETWORK_PROVIDER和GPS_PROVIDER,那么您只需要请求{@code ACCESS_FINE_LOCATION}权限,因为它包含两个提供者的权限。 {@code ACCESS_COARSE_LOCATION}的权限仅允许访问NETWORK_PROVIDER。 注意:如果您的应用程序针对Android 5.0(API级别21)或更高版本,则必须声明您的应用程序在清单文件中使用android.hardware.location.network或者android.hardware.location.gps硬件功能,具体取决于您的应用程序是从NETWORK_PROVIDER还是从GPS_PROVIDER接收位置更新。 如果您的应用程序从这些位置提供商来源收到位置信息,则需要声明该应用程序在应用程序清单中使用这些硬件功能。 在Android 5.0(API 21)之前运行版本的设备上,请求{@code ACCESS_FINE_LOCATION}或{@code ACCESS_COARSE_LOCATION}权限包含对位置硬件功能的隐含请求。 但是,请求这些权限不会自动请求Android 5.0(API级别21)以上的位置硬件功能。 以下代码示例演示了如何在从设备的GPS读取数据的应用程序的清单文件中声明权限和硬件功能: 1 <manifest ... > 2 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 3 ... 4 <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. --> 5 <uses-feature android:name="android.hardware.location.gps" /> 6 ... 7 </manifest>如果不支持BLE,则应优雅地禁用任何BLE功能。 如果支持BLE但禁用,那么您可以请求用户在不离开应用程序的情况下启用蓝牙。 使用BluetoothAdapter可以在两个步骤中完成此设置。
获取BluetoothAdapter BluetoothAdapter是所有蓝牙活动所必需的。 BluetoothAdapter表示设备自己的蓝牙适配器(蓝牙无线电)。 整个系统有一个蓝牙适配器,您的应用程序可以使用此对象进行交互。 下面的代码片段显示了如何获取适配器。 请注意,此方法使用getSystemService()返回一个BluetoothManager实例,然后用于获取适配器。 Android 4.3(API Level 18)介绍了BluetoothManager: 1 private BluetoothAdapter mBluetoothAdapter; 2 ... 3 // Initializes Bluetooth adapter. 4 final BluetoothManager bluetoothManager = 5 (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 6 mBluetoothAdapter = bluetoothManager.getAdapter(); 启用蓝牙 接下来,您需要确保启用蓝牙。 调用isEnabled()来检查蓝牙是否被启用。 如果此方法返回false,则蓝牙被禁用。 以下片段检查是否启用蓝牙。 如果不是,该片段会显示错误并提示用户进入设置以启用蓝牙: 1 // Ensures Bluetooth is available on the device and it is enabled. If not, 2 // displays a dialog requesting user permission to enable Bluetooth. 3 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) 4 { 5 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 6 startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); 7 } 注意:传递给startActivityForResult(android.content.Intent,int)的REQUEST_ENABLE_BT常量,是系统作为requestCode参数在您的onActivityResult(int,int,android.content.Intent) 实现 中,传回给您的本地定义的整数(必须大于0)。
To find BLE devices, you use the startLeScan() method. This method takes a BluetoothAdapter.LeScanCallback as a parameter. You must implement this callback, because that is how scan results are returned. Because scanning is battery-intensive, you should observe the following guidelines:
要查找BLE设备,可以使用startLeScan()方法。 此方法将使用BluetoothAdapter.LeScanCallback作为参数。 您必须实现此回调,因为这是用于定义如何返回扫描结果的方法。 因为扫描是非常消耗电量的,所以您应遵循以下准则: 一旦找到所需的设备,请停止扫描。 切勿循环扫描,并应该设置扫描时间限制。 以前可用的设备可能已经超出扫描范围,持续的扫描会耗光电池电量。 以下片段显示如何启动和停止扫描: 1 /** 2 * Activity for scanning and displaying available BLE devices. 3 */ 4 public class DeviceScanActivity extends ListActivity 5 { 6 private BluetoothAdapter mBluetoothAdapter; 7 private boolean mScanning; 8 private Handler mHandler; 9 10 // Stops scanning after 10 seconds. 11 private static final long SCAN_PERIOD = 10000; 12 ... 13 private void scanLeDevice(final boolean enable) 14 { 15 if (enable) 16 { 17 // Stops scanning after a pre-defined scan period. 18 mHandler.postDelayed(new Runnable() 19 { 20 @Override 21 public void run() 22 { 23 mScanning = false; 24 mBluetoothAdapter.stopLeScan(mLeScanCallback); 25 } 26 }, SCAN_PERIOD); 27 28 mScanning = true; 29 mBluetoothAdapter.startLeScan(mLeScanCallback); 30 } 31 else 32 { 33 mScanning = false; 34 mBluetoothAdapter.stopLeScan(mLeScanCallback); 35 } 36 ... 37 } 38 ... 39 } 如果您只想扫描特定类型的外设,您可以调用startLeScan(UUID [],BluetoothAdapter.LeScanCallback),提供一个指定应用程序支持的GATT服务的UUID对象数组。 以下是BluetoothAdapter.LeScanCallback的实现,它是用于传递BLE扫描结果的接口: 1 private LeDeviceListAdapter mLeDeviceListAdapter; 2 ... 3 // Device scan callback. 4 private BluetoothAdapter.LeScanCallback mLeScanCallback = 5 new BluetoothAdapter.LeScanCallback() 6 { 7 @Override 8 public void onLeScan(final BluetoothDevice device, int rssi, 9 byte[] scanRecord) 10 { 11 runOnUiThread(new Runnable() 12 { 13 @Override 14 public void run() 15 { 16 mLeDeviceListAdapter.addDevice(device); 17 mLeDeviceListAdapter.notifyDataSetChanged(); 18 } 19 }); 20 } 21 }; 注意:如蓝牙所述,您只能扫描蓝牙LE设备或扫描经典蓝牙设备。 您无法同时扫描BLE和经典设备。当一个特定的回调被触发时,它调用相应的broadcastUpdate()辅助方法并传递一个动作。 请注意,本节中的数据解析是根据蓝牙心率测量配置文件规范执行的:
1 private void broadcastUpdate(final String action) 2 { 3 final Intent intent = new Intent(action); 4 sendBroadcast(intent); 5 } 6 7 private void broadcastUpdate(final String action, 8 final BluetoothGattCharacteristic characteristic) 9 { 10 final Intent intent = new Intent(action); 11 12 //这是心率测量配置文件的特殊处理。 根据配置文件规范进行数据解析。 13 if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) 14 { 15 int flag = characteristic.getProperties(); 16 int format = -1; 17 if ((flag & 0x01) != 0) 18 { 19 format = BluetoothGattCharacteristic.FORMAT_UINT16; 20 Log.d(TAG, "Heart rate format UINT16."); 21 } 22 else 23 { 24 format = BluetoothGattCharacteristic.FORMAT_UINT8; 25 Log.d(TAG, "Heart rate format UINT8."); 26 } 27 final int heartRate = characteristic.getIntValue(format, 1); 28 Log.d(TAG, String.format("Received heart rate: %d", heartRate)); 29 intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); 30 } 31 else 32 { 33 // 对于所有其他配置文件,写入以十六进制格式化的数据。 34 final byte[] data = characteristic.getValue(); 35 if (data != null && data.length > 0) 36 { 37 final StringBuilder stringBuilder = new StringBuilder(data.length); 38 for(byte byteChar : data) 39 stringBuilder.append(String.format("X ", byteChar)); 40 intent.putExtra(EXTRA_DATA, new String(data) + "\n" + 41 stringBuilder.toString()); 42 } 43 } 44 sendBroadcast(intent); 45 }回到设备控制活动,这些事件由BroadcastReceiver处理:
1 // Handles various events fired by the Service. 2 // ACTION_GATT_CONNECTED: connected to a GATT server. 3 // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. 4 // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. 5 // ACTION_DATA_AVAILABLE: received data from the device. This can be a 6 // result of read or notification operations. 7 private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() 8 { 9 @Override 10 public void onReceive(Context context, Intent intent) 11 { 12 final String action = intent.getAction(); 13 if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) 14 { 15 mConnected = true; 16 updateConnectionState(R.string.connected); 17 invalidateOptionsMenu(); 18 } 19 else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) 20 { 21 mConnected = false; 22 updateConnectionState(R.string.disconnected); 23 invalidateOptionsMenu(); 24 clearUI(); 25 } 26 else if (BluetoothLeService. 27 ACTION_GATT_SERVICES_DISCOVERED.equals(action)) 28 { 29 // Show all the supported services and characteristics on the 30 // user interface. 31 displayGattServices(mBluetoothLeService.getSupportedGattServices()); 32 } 33 else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) 34 { 35 displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA)); 36 } 37 } 38 };一旦为特征启用通知,如果远程设备上的特性发生变化,则会触发onCharacteristicChanged()回调:
1 @Override 2 // Characteristic notification 3 public void onCharacteristicChanged(BluetoothGatt gatt, 4 BluetoothGattCharacteristic characteristic) 5 { 6 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); 7 }一旦您的应用程序完成使用BLE设备,它应该调用close(),以便系统可以正确释放资源:
1 public void close() 2 { 3 if (mBluetoothGatt == null) 4 { 5 return; 6 } 7 mBluetoothGatt.close(); 8 mBluetoothGatt = null; 9 }
另外推荐几篇有关低功耗蓝牙开发的好文章:
Android BLE学习笔记 :
Android BLE基础框架使用详解
蓝牙自动配对:
Android蓝牙自动配对Demo,亲测好使!!!
