安卓低功耗蓝牙(BLE)开发教程

xiaoxiao2021-02-28  129

翻译的官方文档,原文在这里:点击打开链接 ,本人对部分内容进行了微调,大意和原文一致。

蓝牙低功耗

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权限

       为了在您的应用程序中使用蓝牙功能,您必须声明蓝牙权限BLUETOOTH。 您需要此权限才能执行任何蓝牙通信,例如请求连接,接受连接和传输数据。

       如果您希望应用程序启动设备发现或操作蓝牙设置,您还必须声明BLUETOOTH_ADMIN权限。 注意:如果您使用BLUETOOTH_ADMIN权限,则还必须具有BLUETOOTH权限。

       声明您的应用程序清单文件中的蓝牙权限。如下:

1 <uses-permission android:name="android.permission.BLUETOOTH"/> 2 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

如果您要声明您的应用仅适用于支持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,如果是,则确保已启用。 请注意,只有将<uses-feature ... />设置为false时,才需要进行此检查。

如果不支持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)。

查找BLE设备


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和经典设备。

连接到GATT服务器

与BLE设备交互的第一步是连接到它 - 更具体地说,是连接到设备上的GATT服务器。 要连接到BLE设备上的GATT服务器,可以使用connectGatt()方法。 此方法有三个参数:一个上下文对象,autoConnect(布尔值,指示是否在BLE设备可用时自动连接),以及对BluetoothGattCallback的引用:

1 mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

这个到 GATT服务器的 连接 由BLE设备托管,并返回一个BluetoothGatt实例,然后您可以使用它来进行GATT客户端操作。 调用者(Android应用程式)是GATT客户端。 BluetoothGattCallback用于向客户端提供结果,例如连接状态以及任何进一步的GATT客户端操作。

在此示例中,BLE应用程序提供一个活动(DeviceControlActivity)来连接,显示数据,并显示GATT服务和设备支持的特性。 根据用户输入,此活动与名为BluetoothLeService的服务进行通信,该服务通过Android BLE API与BLE设备进行交互:

1 // A service that interacts with the BLE device via the Android BLE API. 2 public class BluetoothLeService extends Service 3 { 4     private final static String TAG = BluetoothLeService.class.getSimpleName(); 5 6     private BluetoothManager mBluetoothManager; 7     private BluetoothAdapter mBluetoothAdapter; 8     private String mBluetoothDeviceAddress; 9     private BluetoothGatt mBluetoothGatt; 10     private int mConnectionState = STATE_DISCONNECTED; 11 12     private static final int STATE_DISCONNECTED = 0; 13     private static final int STATE_CONNECTING = 1; 14     private static final int STATE_CONNECTED = 2; 15 16     public final static String ACTION_GATT_CONNECTED = 17             "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; 18     public final static String ACTION_GATT_DISCONNECTED = 19             "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; 20     public final static String ACTION_GATT_SERVICES_DISCOVERED = 21             "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; 22     public final static String ACTION_DATA_AVAILABLE = 23             "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; 24     public final static String EXTRA_DATA = 25             "com.example.bluetooth.le.EXTRA_DATA"; 26 27     public final static UUID UUID_HEART_RATE_MEASUREMENT = 28             UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); 29 30     // Various callback methods defined by the BLE API. 31     private final BluetoothGattCallback mGattCallback = 32             new BluetoothGattCallback() 33   { 34         @Override 35         public void onConnectionStateChange(BluetoothGatt gatt, int status, 36                 int newState) 37       { 38             String intentAction; 39             if (newState == BluetoothProfile.STATE_CONNECTED) 40           { 41                 intentAction = ACTION_GATT_CONNECTED; 42                 mConnectionState = STATE_CONNECTED; 43                 broadcastUpdate(intentAction); 44                 Log.i(TAG, "Connected to GATT server."); 45                 Log.i(TAG, "Attempting to start service discovery:" + 46                         mBluetoothGatt.discoverServices()); 47 48             } 49            else if (newState == BluetoothProfile.STATE_DISCONNECTED) 50           { 51                 intentAction = ACTION_GATT_DISCONNECTED; 52                 mConnectionState = STATE_DISCONNECTED; 53                 Log.i(TAG, "Disconnected from GATT server."); 54                 broadcastUpdate(intentAction); 55             } 56         } 57 58         @Override 59         // New services discovered 60         public void onServicesDiscovered(BluetoothGatt gatt, int status) 61       { 62             if (status == BluetoothGatt.GATT_SUCCESS) 63           { 64                 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); 65             } 66            else 67           { 68                 Log.w(TAG, "onServicesDiscovered received: " + status); 69             } 70         } 71 72         @Override 73         // Result of a characteristic read operation 74         public void onCharacteristicRead(BluetoothGatt gatt, 75                 BluetoothGattCharacteristic characteristic, 76                 int status) 77       { 78             if (status == BluetoothGatt.GATT_SUCCESS) 79           { 80                 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); 81             } 82         } 83      ... 84     }; 85 ... 86 }

当一个特定的回调被触发时,它调用相应的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 };

阅读BLE属性

一旦您的Android应用程序连接到GATT服务器并发现服务,它就可以在支持的情况下读取和写入属性。 例如,此代码段遍历服务器的服务和特征,并在UI中显示它们:

1 public class DeviceControlActivity extends Activity 2 { 3     ... 4    //演示如何迭代支持的GATT服务/特性。 5    //在此示例中,我们填充绑定到UI上的ExpandableListView的数据结构。 6     private void displayGattServices(List<BluetoothGattService> gattServices) 7   { 8         if (gattServices == null) return; 9         String uuid = null; 10         String unknownServiceString = getResources(). 11                 getString(R.string.unknown_service); 12         String unknownCharaString = getResources(). 13                 getString(R.string.unknown_characteristic); 14         ArrayList<HashMap<String, String>> gattServiceData = 15                 new ArrayList<HashMap<String, String>>(); 16         ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData 17                 = new ArrayList<ArrayList<HashMap<String, String>>>(); 18         mGattCharacteristics = 19                 new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); 20 21         // 通过可用的GATT服务循环。 22         for (BluetoothGattService gattService : gattServices) 23       { 24             HashMap<String, String> currentServiceData = 25                     new HashMap<String, String>(); 26             uuid = gattService.getUuid().toString(); 27             currentServiceData.put( 28                     LIST_NAME, SampleGattAttributes. 29                             lookup(uuid, unknownServiceString)); 30             currentServiceData.put(LIST_UUID, uuid); 31             gattServiceData.add(currentServiceData); 32 33             ArrayList<HashMap<String, String>> gattCharacteristicGroupData = 34                     new ArrayList<HashMap<String, String>>(); 35             List<BluetoothGattCharacteristic> gattCharacteristics = 36                     gattService.getCharacteristics(); 37             ArrayList<BluetoothGattCharacteristic> charas = 38                     new ArrayList<BluetoothGattCharacteristic>(); 39            // 循环通过可用特性。 40             for (BluetoothGattCharacteristic gattCharacteristic : 41                     gattCharacteristics) 42           { 43                 charas.add(gattCharacteristic); 44                 HashMap<String, String> currentCharaData = 45                         new HashMap<String, String>(); 46                 uuid = gattCharacteristic.getUuid().toString(); 47                 currentCharaData.put( 48                         LIST_NAME, SampleGattAttributes.lookup(uuid, 49                                 unknownCharaString)); 50                 currentCharaData.put(LIST_UUID, uuid); 51                 gattCharacteristicGroupData.add(currentCharaData); 52             } 53             mGattCharacteristics.add(charas); 54             gattCharacteristicData.add(gattCharacteristicGroupData); 55          } 56     ... 57     } 58 ... 59 }

接收GATT通知

BLE应用程序通常会在设备上的特定特性发生变化时要求收到通知。 此代码段显示了如何使用setCharacteristicNotification()方法设置特征的通知:

1 private BluetoothGatt mBluetoothGatt; 2 BluetoothGattCharacteristic characteristic; 3 boolean enabled; 4 ... 5 mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); 6 ... 7 BluetoothGattDescriptor descriptor = characteristic.getDescriptor( 8         UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); 9 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 10 mBluetoothGatt.writeDescriptor(descriptor);

一旦为特征启用通知,如果远程设备上的特性发生变化,则会触发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,亲测好使!!!  

转载请注明原文地址: https://www.6miu.com/read-69070.html

最新回复(0)