ToB企服应用市场:ToB评测及商务社交产业平台

标题: Android BLE 的扫描配对、连接流程梳理 [打印本页]

作者: 风雨同行    时间: 2024-11-9 06:51
标题: Android BLE 的扫描配对、连接流程梳理
目次
一、前提条件
二、扫描BLE装备
2.1.BLE扫描的模式
2.1.1. 主动扫描(Active Scanning)
2.1.2. 被动扫描(Passive Scanning)
2.1.3. 注意事项
2.2. 扫描流程的重要步调
2.2.1. 获取蓝牙适配器
2.2.2. 开启蓝牙
2.2.3. 处理用户相应并开始扫描
2.2.4. 处理扫描结果
2.2.5. 制止扫描
三、配对BLE装备(可选)
3.1. 配对过程
3.1.1. 发起配对请求
3.1.2. 监听配对状态
3.1.3. 处理配对结果
3.1.4. 更新UI或实行后续操作
3.1.5. 注销BroadcastReceiver
3.2. 代码示例
3.3. 注意事项 
四、连接BLE装备
4.1. 连接到GATT Server
4.2. 处理连接回调
4.3. 发现服务
4.4. 读写数据
五、断开连接
5.1. 调用disconnect()方法
5.2. 设置标记以跟踪连接状态
5.3. 开释BluetoothGatt对象
5.4. 清理其他资源
5.5. 注意事项
六、总结

Android BLE(Bluetooth Low Energy,低功耗蓝牙)连接流程涉及多个步调,重要包罗扫描、配对、连接三个阶段。以下是具体的流程梳理。
一、前提条件

1.1. 装备支持:确保Android装备支持BLE功能。从Android 4.3(API 级别 18)开始,Android体系内置了对BLE的支持。
1.2. 权限申请:在AndroidManifest.xml中申请必要的权限,包罗BLUETOOTH、BLUETOOTH_ADMIN、BLUETOOTH_SCAN和BLUETOOTH_CONNECT等。
1.3. 动态权限请求:对于Android 6.0(API 级别 23)及以上版本,还必要在运行时请求位置权限(ACCESS_COARSE_LOCATION或ACCESS_FINE_LOCATION),因为从Android 6.0开始,蓝牙扫描必要位置权限。
二、扫描BLE装备

在BLE创建连接之前,必要举行装备之间的扫描。BLE扫描重要分为两种模式:主动扫描(Active Scanning)和被动扫描(Passive Scanning)。这两种扫描模式在功耗、扫描范围和相应方式上存在明显差异。
2.1.BLE扫描的模式

2.1.1. 主动扫描(Active Scanning)

特点

应用场景

2.1.2. 被动扫描(Passive Scanning)

特点

应用场景

2.1.3. 注意事项


2.2. 扫描流程的重要步调

2.2.1. 获取蓝牙适配器

通过BluetoothManager获取BluetoothAdapter对象。使用BluetoothAdapter的isEnabled()方法来检查蓝牙是否已经开启。
  1. BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  
  2. if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {  
  3.     // 蓝牙未开启  
  4. }
复制代码
2.2.2. 开启蓝牙

如果蓝牙未开启,可以通过启动一个Intent来请求用户启用蓝牙。这通常会导致体系表现一个对话框,扣问用户是否答应启用蓝牙。
  1. if (!bluetoothAdapter.isEnabled()) {  
  2.     Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
  3.     startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);  
  4. }
复制代码
此中,REQUEST_ENABLE_BT是一个自定义的请求码,用于在onActivityResult()方法中辨认这个特定的请求。这样,当用户相应蓝牙启用请求后,应用就可以知道用户是否答应了蓝牙的启用。
2.2.3. 处理用户相应并开始扫描

重写onActivityResult()方法来处理用户对蓝牙启用请求的相应。
  1. @Override  
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.     if (requestCode == REQUEST_ENABLE_BT) {  
  4.         if (resultCode == Activity.RESULT_OK) {  
  5.             // 蓝牙已启用,继续执行BLE操作  
  6.         } else {  
  7.             // 用户拒绝了蓝牙的启用,处理这种情况  
  8.             Toast.makeText(this, "蓝牙未开启,无法继续操作", Toast.LENGTH_SHORT).show();  
  9.         }  
  10.     }  
  11.     super.onActivityResult(requestCode, resultCode, data);  
  12. }
复制代码
如果用户答应了蓝牙的启用,可以继续实行BLE扫描和连接操作。使用BluetoothAdapter的getBluetoothLeScanner()方法获取BluetoothLeScanner对象,并调用其startScan()方法开始扫描BLE装备。可以设置扫描参数,如扫描模式(SCAN_MODE_LOW_LATENCY、SCAN_MODE_BALANCED、SCAN_MODE_LOW_POWER)和报告耽误等。
BluetoothLeScanner 类是用于实行BLE装备扫描的核心类,该类中的startScan方法答应以更灵活和强大的方式开始BLE装备的扫描。可以指定扫描过滤器(ScanFilter)、扫描设置(ScanSettings)以及扫描回调(ScanCallback),以便在发现装备时吸收关照。
  以下是startScan方法的一些重载版本及其简要分析:

  1. List<ScanFilter> filters = new ArrayList<>();  
  2. // 可以根据需要添加ScanFilter到列表中  
  3. ScanSettings settings = new ScanSettings.Builder()  
  4.     .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 设置扫描模式  
  5.     .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // 设置回调类型  
  6.     .build();  
  7. ScanCallback scanCallback = new ScanCallback() {  
  8.     @Override  
  9.     public void onScanResult(int callbackType, ScanResult result) {  
  10.         super.onScanResult(callbackType, result);  
  11.         // 处理扫描结果  
  12.     }  
  13.     @Override  
  14.     public void onBatchScanResults(List<ScanResult> results) {  
  15.         super.onBatchScanResults(results);  
  16.         // 处理批量扫描结果(如果启用了批量扫描)  
  17.     }  
  18.     @Override  
  19.     public void onScanFailed(int errorCode) {  
  20.         super.onScanFailed(errorCode);  
  21.         // 处理扫描失败的情况  
  22.     }  
  23. };  
  24. bluetoothLeScanner.startScan(filters, settings, scanCallback);
复制代码

这个版本的startScan方法通过PendingIntent来吸收扫描结果,这种方法答应应用在不直接运行的情况下(比方,当应用处于背景或被体系挂起时),通过广播吸收器(Broadcast Receiver)或其他方式吸收扫描到的BLE装备信息。然而,在大多数情况下,直接使用ScanCallback会更简朴、更直接。
下面是一个根本的示例:起首,必要创建一个 PendingIntent,它指向一个可以或许吸收扫描结果的广播吸收器(Broadcast Receiver):
  1. Intent intent = new Intent(context, YourBroadcastReceiver.class);  
  2. PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
复制代码
然后,使用 startScan 方法,并传入扫描过滤器、扫描设置和上面创建的 PendingIntent:
  1. List<ScanFilter> filters = new ArrayList<>();  
  2. // 如果需要,可以向filters列表中添加ScanFilter  
  3.   
  4. ScanSettings settings = new ScanSettings.Builder()  
  5.     .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)  
  6.     .build();  
  7.   
  8. bluetoothLeScanner.startScan(filters, settings, pendingIntent);
复制代码
 在广播吸收器中,必要处理吸收到的扫描结果。但是,必要注意的是,BLE扫描的 PendingIntent 并不直接传递扫描结果作为Intent的额外数据(extras)。相反,当扫描到匹配的装备时,体系会发送一个带有特定动作(action)的广播,而广播吸收器必要监听这个action,并大概必要通过其他方式(如查询最新的扫描结果)来获取具体的装备信息。
2.2.4. 处理扫描结果

通过BluetoothLeScanner.ScanCallback的onScanResult(int callbackType, ScanResult result)回调中,可以处理扫描到的BLE装备信息。这个回调方法会在每次扫描到新的BLE装备或已扫描到的装备更新了其广播数据时被调用。以下是如安在onScanResult回调中处理BLE装备信息的根本步调:
  1. ScanCallback scanCallback =
  2. new ScanCallback() {
  3.     @Override
  4.     public void onScanResult(int callbackType, ScanResult result) {
  5.         if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
  6.             // Should not happen.
  7.             Log.e(TAG, "LE Scan has already started");
  8.             return;
  9.         }
  10.         ScanRecord scanRecord = result.getScanRecord();
  11.         if (scanRecord == null) {
  12.             return;
  13.         }
  14.         if (serviceUuids != null) {
  15.             List<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
  16.             for (UUID uuid : serviceUuids) {
  17.                 uuids.add(new ParcelUuid(uuid));
  18.             }
  19.             List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
  20.             if (scanServiceUuids == null
  21.                     || !scanServiceUuids.containsAll(uuids)) {
  22.                 if (DBG) {
  23.                     Log.d(TAG, "uuids does not match");
  24.                 }
  25.                 return;
  26.             }
  27.         }
  28.         callback.onLeScan(
  29.                 result.getDevice(), result.getRssi(), scanRecord.getBytes());
  30.     }
  31. };
复制代码
2.2.5. 制止扫描

在得当的时候(比如找到了目标装备或者用户请求制止扫描时),调用BluetoothLeScanner的stopScan()方法来制止扫描。这有助于镌汰不必要的功耗,并避免在不再必要时继续吸收扫描结果。
  1. // 停止扫描  
  2. scanner.stopScan(scanCallback);
复制代码
  注意:在上面的stopScan()调用中,scanCallback是传递给startScan()方法的同一个ScanCallback实例。然而,从Android API 21(Lollipop)开始,stopScan()方法现实上并不要传递任何参数。如果没有引用到特定的ScanCallback实例,或者只是想制止所有扫描(无论是由哪个ScanCallback触发的),可以简朴地调用无参数的stopScan()方法。
  1. // 更简单的停止扫描方式(不需要传递ScanCallback)  
  2. scanner.stopScan();
复制代码
现实开发中结合具体需求和Android API版本选择得当的方法。
三、配对BLE装备(可选)

BLE装备通常不必要像经典蓝牙那样举行配对,因为它们通过密钥交换来确保通信安全。但是,某些BLE装备大概要求配对或绑定操作,可以通过调用BluetoothDevice对象的createBond()方法来发起配对请求。这个过程对于必要更高安全性的通信场景尤为重要,因为配对过程中会生成并存储密钥,这些密钥在将来的连接中用于身份验证和加密通信,从而掩护数据的机密性和完整性。
3.1. 配对过程

3.1.1. 发起配对请求

通过调用BluetoothDevice的createBond()方法,可以向长途蓝牙装备发起配对请求。这个方法会返回一个布尔值,但在Android中,由于配对是异步举行的,这个返回值通常不会被用来判断配对是否成功。相反,为了确定配对是否成功,必要注册一个BroadcastReceiver来监听BluetoothDevice.ACTION_BOND_STATE_CHANGED这一体系广播。当蓝牙装备的配对状态发生变化时(如从未配对变为正在配对,或从正在配对变为已配对或配对失败),体系会发送这个广播。
在BroadcastReceiver的onReceive()方法中,通过检查Intent中的BluetoothDevice.EXTRA_DEVICE和BluetoothDevice.EXTRA_BOND_STATE来获取发生状态变化的蓝牙装备和其新的配对状态。配对状态可以是以下几种之一:
   
  通过检查这些状态,你可以在UI中相应地更新状态,或者在配对成功后实行其他操作(如创建BLE连接)。
此外,值得注意的是,在某些情况下(如用户取消配对请求或装备不支持配对),配对大概会失败。在这些情况下,你也应该在UI中向用户表现得当的错误消息。
最后,别忘了在不再必要监听配对状态变化时注销BroadcastReceiver,以避免内存泄漏或其他潜伏标题。
3.1.2. 监听配对状态

为了获知配对的状态,必要注册一个BroadcastReceiver来监听BluetoothDevice.EXTRA_BOND_STATE和BluetoothDevice.EXTRA_DEVICE这两个Intent extra来获取配对状态和相关的BluetoothDevice对象。
3.1.3. 处理配对结果


3.1.4. 更新UI或实行后续操作

根据配对状态,可以在UI中表现相应的消息,或者实行其他必要的操作(如实验创建BLE连接)。
3.1.5. 注销BroadcastReceiver

当不再必要监听配对状态变化时(比方,当用户离开包含蓝牙功能的Activity时),应该注销BroadcastReceiver以避免内存泄漏。
3.2. 代码示例

下面是一个简化的代码示例,展示了如何调用createBond()方法并注册一个BroadcastReceiver来监听配对状态变化:
  1. // 假设已经有了BluetoothDevice对象 bluetoothDevice  
  2.   
  3. // 调用createBond()方法(注意:这个方法不返回配对结果)  
  4. boolean result = bluetoothDevice.createBond();  
  5. // 注意:这里的result只是表示请求是否已经被系统接收,并不表示配对是否成功  
  6.   
  7. // 注册BroadcastReceiver来监听配对状态变化  
  8. IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);  
  9. registerReceiver(mBondStateChangeReceiver, filter);  
  10.   
  11. // BroadcastReceiver的实现  
  12. private final BroadcastReceiver mBondStateChangeReceiver = new BroadcastReceiver() {  
  13.     @Override  
  14.     public void onReceive(Context context, Intent intent) {  
  15.         final String action = intent.getAction();  
  16.         if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {  
  17.             final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
  18.             if (device == bluetoothDevice) {  
  19.                 final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);  
  20.                 // 处理配对状态变化...  
  21.                 switch (state) {  
  22.                     case BluetoothDevice.BOND_NONE:  
  23.                         // 未配对  
  24.                         break;  
  25.                     case BluetoothDevice.BOND_BONDING:  
  26.                         // 正在配对  
  27.                         break;  
  28.                     case BluetoothDevice.BOND_BONDED:  
  29.                         // 已配对  
  30.                         break;  
  31.                 }  
  32.             }  
  33.         }  
  34.     }  
  35. };  
  36.   
  37. // 不要忘记在适当的时候注销BroadcastReceiver  
  38. unregisterReceiver(mBondStateChangeReceiver);
复制代码
3.3. 注意事项 

   
  四、连接BLE装备

连接到BLE装备的GATT Server并与之交互的过程,通常包罗连接装备、发现服务、读写数据等步调。以下是一个简化的流程分析,以及如安在Android应用中使用这些步调的示例代码。
4.1. 连接到GATT Server

起首,必要有一个BluetoothDevice对象,通常是通过扫描BLE装备并选择一个来得到的。然后,可以使用connectGatt()方法来实验连接到该装备的GATT Server。
  1. BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback);
复制代码
onnectGatt方法参数和返回值的分析:


4.2. 处理连接回调

在BluetoothGattCallback的onConnectionStateChange()方法中处理连接状态的改变。当连接状态变为BluetoothGatt.STATE_CONNECTED时,表示连接成功。
  1. private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {  
  2.     @Override  
  3.     public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {  
  4.         if (newState == BluetoothProfile.STATE_CONNECTED) {  
  5.             // 连接成功,开始发现服务  
  6.             gatt.discoverServices();  
  7.         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {  
  8.             // 连接丢失,处理断开连接的情况  
  9.             // ...  
  10.         }  
  11.     }  
  12.   
  13.     // 其他回调方法,如onServicesDiscovered(), onCharacteristicRead()等  
  14. };
复制代码
4.3. 发现服务

连接成功后,调用BluetoothGatt的discoverServices()方法来发现装备提供的所有服务。服务发现的结果将在onServicesDiscovered()回调中返回。
  1. @Override  
  2. public void onServicesDiscovered(BluetoothGatt gatt, int status) {  
  3.     if (status == BluetoothGatt.GATT_SUCCESS) {  
  4.         // 服务发现成功,可以开始读取或写入数据  
  5.         // 遍历gatt.getServices()获取服务列表  
  6.         // ...  
  7.     } else {  
  8.         // 服务发现失败  
  9.         // ...  
  10.     }  
  11. }
复制代码
4.4. 读写数据

通过服务中的特性(Characteristic)来读写数据。可以使用BluetoothGatt的readCharacteristic()或writeCharacteristic()方法来举行数据的读写操作。
  1. // 假设你已经找到了一个BluetoothGattCharacteristic对象名为characteristic  
  2.   
  3. // 读取数据  
  4. bluetoothGatt.readCharacteristic(characteristic);  
  5.   
  6. // 写入数据  
  7. characteristic.setValue(data); // data是一个byte[],包含你想要写入的数据  
  8. bluetoothGatt.writeCharacteristic(characteristic);  
  9.   
  10. // 注意:写操作可能需要在`onCharacteristicWrite()`回调中确认是否成功
复制代码
五、断开连接

5.1. 调用disconnect()方法

在不再必要BLE连接时,应调用BluetoothGatt的disconnect()方法来断开连接。
  1. if (bluetoothGatt != null) {  
  2.     bluetoothGatt.disconnect();  
  3. }
复制代码
5.2. 设置标记以跟踪连接状态

同样地,由于断开连接是异步的,大概必要设置一个标记来跟踪连接状态,并在onConnectionStateChange()回调中处理断开连接后的逻辑。
  1. private boolean isConnected = false;  
  2.   
  3. // ...  
  4.   
  5. @Override  
  6. public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {  
  7.     if (newState == BluetoothProfile.STATE_CONNECTED) {  
  8.         isConnected = true;  
  9.         // 连接成功,开始发现服务  
  10.         gatt.discoverServices();  
  11.     } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {  
  12.         isConnected = false;  
  13.         // 连接已断开,清理资源  
  14.         if (gatt != null) {  
  15.             gatt.close();  
  16.             bluetoothGatt = null; // 避免内存泄漏  
  17.         }  
  18.         // 处理断开连接后的逻辑  
  19.         // ...  
  20.     }  
  21. }
复制代码
5.3. 开释BluetoothGatt对象

一旦连接状态变为STATE_DISCONNECTED,并且确定不再必要BluetoothGatt对象,就应该调用close()方法来开释它。这有助于避免资源泄漏,特别是如果应用大概会频仍地连接和断开BLE装备。
在上面的onConnectionStateChange()回调示例中,当连接状态变为STATE_DISCONNECTED时,我们调用了gatt.close()并将bluetoothGatt设置为null,以确保没有引用指向该对象,从而答应垃圾接纳器接纳它。
5.4. 清理其他资源

除了BluetoothGatt对象外,如果应用还使用了其他与BLE相关的资源(如线程、定时器、监听器等),也应该在断开连接后得当地清理它们。
5.5. 注意事项

   
  以上即为Android BLE蓝牙的扫描、配对与连接流程的大致梳理。必要注意的是,不同装备和不同应用场景下,具体的实现细节大概会有所不同。
六、总结

综上所述,Android BLE开发涉及扫描、配对(可选)、连接三个阶段。扫描分为主动和被动,需思量功耗和获取信息的具体水平。配对可选,涉及发起请求、监听状态和处理结果。连接前需确保蓝牙开启和权限申请,连接成功后需发现服务并读写数据。整个流程异步处理,通过回调返回结果。BLE通信需关注装备兼容性、安全性和性能优化。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4