0

0

Android BLE AdvertisingSet 扫描响应数据发送指南

聖光之護

聖光之護

发布时间:2025-08-14 22:48:17

|

620人浏览过

|

来源于php中文网

原创

Android BLE AdvertisingSet 扫描响应数据发送指南

本教程详细阐述了在 Android BLE AdvertisingSet 中正确配置和发送扫描响应(Scan Response)数据的方法。核心在于确保 AdvertisingSetParameters 中设置 setScannable(true),以允许设备响应扫描请求并发送包含额外信息的扫描响应包。文章将通过代码示例和注意事项,指导开发者有效利用 AdvertisingSet 传输完整的 BLE 广告数据。

1. 理解 Android BLE 广告与扫描响应

低功耗蓝牙(ble)通信始于广告(advertising)过程。一个 ble 设备(advertiser)通过广播广告包(advertisement packet)来宣布其存在和提供的信息。这些广告包通常包含设备名称、服务 uuid 等基本信息。然而,广告包的大小是有限的(通常为 31 字节)。

为了传输更多数据,BLE 引入了扫描响应包(Scan Response Packet)的概念。当一个扫描器(Scanner)执行“主动扫描”(Active Scan)时,它会发送一个扫描请求(Scan Request)给正在广播的设备。如果该设备被配置为可响应扫描请求,它将发送一个扫描响应包,其中可以包含额外的设备信息,如制造商特定数据、更长的设备名称或服务数据等。

从 Android 8.0 (API 26) 开始,Google 引入了 AdvertisingSet API,提供了更灵活和强大的 BLE 广告能力,允许开发者同时管理多个广告集,并支持 LE Coded PHY 和 LE 2M PHY 等新特性。

2. 核心问题:扫描响应数据未发送的原因

在 AdvertisingSet 中,即使开发者通过 startAdvertisingSet 方法明确提供了 AdvertiseData 作为扫描响应包,扫描器端仍可能无法接收到这些数据。常见的问题表现为,扫描器接收到的 ScanResult 中,ScanRecord 对象的 mServiceData 或其他本应包含在扫描响应中的字段为空。

导致这一问题的根本原因在于,AdvertisingSetParameters 中缺少一个关键的配置:setScannable(true)。默认情况下,如果未明确设置,设备可能不会响应扫描请求,从而导致扫描响应数据无法发送。setScannable(true) 明确告诉 BLE 协议栈,该广告集是可被扫描的,并且应该在收到扫描请求时发送扫描响应。

3. 正确配置 AdvertisingSet 发送扫描响应

要确保 AdvertisingSet 能够成功发送扫描响应数据,需要正确配置 AdvertisingSetParameters、主广告数据和扫描响应数据,并启动广告集。

3.1 配置 AdvertisingSetParameters

AdvertisingSetParameters 定义了广告集的行为,包括是否可连接、是否使用传统模式、广告间隔和发射功率等。要发送扫描响应,最关键的一步是调用 setScannable(true)。

import android.bluetooth.le.AdvertisingSetParameters;

// ... (假设 serviceId 已经定义为 ParcelUuid)

AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder()
        .setConnectable(false) // 根据需求设置是否可连接。此处设置为不可连接。
        .setLegacyMode(true)   // 是否使用传统广告模式(ADV_IND, ADV_SCAN_IND等)。
                               // 即使在传统模式下,setScannable(true) 也是发送扫描响应的关键。
        .setScannable(true)    // 关键:允许设备响应扫描请求并发送扫描响应。
        .setInterval(AdvertisingSetParameters.INTERVAL_HIGH) // 广告间隔,例如高频间隔。
        .setTxPowerLevel(AdvertisingSetParameters.TX_POWER_MEDIUM) // 发射功率。
        .build();

3.2 构建主广告数据 AdvertiseData

主广告数据是设备广播的基本信息,扫描器即使不发送扫描请求也能接收到。

import android.bluetooth.le.AdvertiseData;
import android.os.ParcelUuid;

// ... (假设 serviceId 已经定义为 ParcelUuid)

AdvertiseData advertiseData = new AdvertiseData.Builder()
        .setIncludeDeviceName(true) // 是否在主广告包中包含设备名称。
        .setIncludeTxPowerLevel(false) // 是否在主广告包中包含发射功率。
        .addServiceUuid(serviceId) // 添加服务 UUID。
        .build();

3.3 构建扫描响应数据 AdvertiseData

扫描响应数据包含在主广告包中无法容纳的额外信息。这些数据只会在扫描器发送扫描请求后才被发送。

萝卜简历
萝卜简历

免费在线AI简历制作工具,帮助求职者轻松完成简历制作。

下载
import java.nio.charset.StandardCharsets;

// ... (假设 serviceId 已经定义为 ParcelUuid)

byte[] packetContent = "hello".getBytes(StandardCharsets.UTF_8); // 示例:要发送的服务数据内容。

AdvertiseData scanResponseData = new AdvertiseData.Builder()
        .setIncludeDeviceName(false) // 扫描响应中通常不需要再次包含设备名称,除非有特定需求。
        .setIncludeTxPowerLevel(false) // 扫描响应中通常不需要再次包含发射功率。
        .addServiceUuid(serviceId) // 再次添加服务 UUID,或添加其他服务 UUID。
        .addServiceData(serviceId, packetContent) // 添加服务数据,这是本例中期望传输的关键信息。
        .build();

3.4 启动广告集 startAdvertisingSet

最后,使用 BluetoothLeAdvertiser 实例启动广告集,传入之前构建的参数和数据。

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.AdvertisingSetCallback;
import android.bluetooth.le.AdvertisingSet;
import android.util.Log;

// ... (假设 parameters, advertiseData, scanResponseData 已经定义)
// ... (假设 serviceId 已经定义为 ParcelUuid)

BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();

if (advertiser == null) {
    Log.e("BLEAdvertiser", "BluetoothLeAdvertiser not available.");
    return;
}

AdvertisingSetCallback advertisingSetCallback = new AdvertisingSetCallback() {
    @Override
    public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) {
        Log.d("BLEAdvertiser", "AdvertisingSet started, status: " + status);
        // 可以在此处保存 advertisingSet 实例以便后续停止广告
    }

    @Override
    public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
        Log.d("BLEAdvertiser", "Advertising data set, status: " + status);
    }

    @Override
    public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {
        Log.d("BLEAdvertiser", "Scan response data set, status: " + status);
    }

    @Override
    public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
        Log.d("BLEAdvertiser", "AdvertisingSet stopped.");
    }

    @Override
    public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {
        Log.d("BLEAdvertiser", "Advertising enabled: " + enable + ", status: " + status);
    }

    // ... 其他回调方法,如 onAdvertisingSetStopped, onAdvertisingEnabled 等
};

// 启动广告集
advertiser.startAdvertisingSet(parameters, advertiseData, scanResponseData, null, null, advertisingSetCallback);

4. 验证:通过 BLE 扫描器接收扫描响应

为了验证扫描响应是否成功发送,需要配置一个 BLE 扫描器,并确保其执行的是主动扫描。

4.1 配置 ScanFilter 和 ScanSettings

扫描器需要设置 ScanSettings 以指定扫描模式。SCAN_MODE_LOW_LATENCY 是一个主动扫描模式,它会发送扫描请求并等待扫描响应。

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.bluetooth.le.ScanRecord;
import android.os.ParcelUuid;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

// ... (假设 serviceId 已经定义为 ParcelUuid)

List filters = new ArrayList<>();
ScanFilter filter = new ScanFilter.Builder()
        .setServiceUuid(serviceId) // 根据服务 UUID 过滤设备。
        .build();
filters.add(filter);

// 设置扫描模式为低延迟,这通常意味着主动扫描。
ScanSettings scanSettings = new ScanSettings.Builder()
        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 确保是主动扫描,以便接收扫描响应。
        .build();

4.2 处理 ScanResult

在 ScanCallback 的 onScanResult 方法中,可以通过 ScanResult.getScanRecord() 获取 ScanRecord 对象,然后从中提取 ServiceData 来验证扫描响应是否成功接收。

// ... (假设 filters, scanSettings 已经定义)
// ... (假设 serviceId 已经定义为 ParcelUuid)

BluetoothLeScanner bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();

if (bluetoothLeScanner == null) {
    Log.e("BLEScanner", "BluetoothLeScanner not available.");
    return;
}

ScanCallback scanCallback = new ScanCallback() {
    private static final String TAG = "BLEScanner";

    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        ScanRecord scanRecord = result.getScanRecord();
        if (scanRecord != null) {
            // 打印完整的 ScanRecord 信息以便调试
            Log.d(TAG, "ScanResult: " + result.toString());

            // 检查 ServiceData 是否包含期望的内容
            Map serviceData = scanRecord.getServiceData();
            if (serviceData != null && serviceData.containsKey(serviceId)) {
                byte[] receivedData = serviceData.get(serviceId);
                String dataString = new String(receivedData, StandardCharsets.UTF_8);
                Log.d(TAG, "Received ServiceData for UUID " + serviceId.toString() + ": " + dataString);
                // 验证收到的数据是否为 "hello"
                if ("hello".equals(dataString)) {
                    Log.d(TAG, "Scan response data received successfully!");
                }
            } else {
                Log.d(TAG, "ServiceData for UUID " + serviceId.toString() + " not found in scan response.");
            }
        }
    }

    @Override
    public void onBatchScanResults(List results) {
        super.onBatchScanResults(results);
        // 处理批量扫描结果
    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Log.e(TAG, "Scan failed with error code: " + errorCode);
    }
};

// 启动扫描
bluetoothLeScanner.startScan(filters, scanSettings, scanCallback);

// 在适当的时候停止扫描
// bluetoothLeScanner.stopScan(scanCallback);

5. 注意事项与最佳实践

  • 主动扫描与被动扫描:再次强调,扫描响应只会在扫描器执行主动扫描时发送。被动扫描(如 SCAN_MODE_LOW_POWER 或 SCAN_MODE_BALANCED 的某些配置)只会接收主广告包。
  • legacyMode 的影响:即使在 AdvertisingSetParameters 中设置 setLegacyMode(true),表示使用传统广告格式,setScannable(true) 仍然是确保扫描响应发送的关键。AdvertisingSet API 提供了对传统广告模式的封装和更精细的控制。
  • 数据大小限制:无论是主广告包还是扫描响应包,其有效载荷大小都受到 BLE 规范的限制,通常为 31 字节。如果需要传输更多数据,可能需要考虑 GATT 服务或分包传输。
  • 权限:确保 AndroidManifest.xml 中声明了必要的蓝牙权限:
    
    
    
    
    
     

    对于 Android 12 (API 31) 及更高版本,需要 BLUETOOTH_ADVERTISE, BLUETOOTH_SCAN, BLUETOOTH_CONNECT。对于 Android 11 (API 30) 及更低版本,通常需要 BLUETOOTH, BLUETOOTH_ADMIN 和 ACCESS_FINE_LOCATION。运行时权限请求也是必不可少的。

  • 错误处理与回调:在 AdvertisingSetCallback 中,务必实现 onAdvertisingSetStarted、onAdvertisingSetStopped 等回调方法,以便监控广告集的状态和处理可能发生的错误(如 status 参数非 ADVERTISE_SUCCESS)。

总结

通过本教程,我们深入探讨了 Android BLE AdvertisingSet 中扫描响应数据未发送的问题,并提供了详细的解决方案。核心要点在于,当使用 AdvertisingSet 并期望发送扫描响应数据时,必须在 AdvertisingSetParameters.Builder 中明确调用 setScannable(true)。同时,扫描器端也需要执行主动扫描才能请求并接收这些响应数据。遵循这些指导原则,开发者可以有效利用 AdvertisingSet 的强大功能,实现更丰富和灵活的 BLE 广告通信。

相关专题

更多
pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1874

2024.04.01

xml怎么变成word
xml怎么变成word

步骤:1. 导入 xml 文件;2. 选择 xml 结构;3. 映射 xml 元素到 word 元素;4. 生成 word 文档。提示:确保 xml 文件结构良好,并预览 word 文档以验证转换是否成功。想了解更多xml的相关内容,可以阅读本专题下面的文章。

2085

2024.08.01

xml是什么格式的文件
xml是什么格式的文件

xml是一种纯文本格式的文件。xml指的是可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。想了解更多相关的内容,可阅读本专题下面的相关文章。

991

2024.11.28

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

387

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

571

2023.08.10

android开发三大框架
android开发三大框架

android开发三大框架是XUtil框架、volley框架、ImageLoader框架。本专题为大家提供android开发三大框架相关的各种文章、以及下载和课程。

267

2023.08.14

android是什么系统
android是什么系统

Android是一种功能强大、灵活可定制、应用丰富、多任务处理能力强、兼容性好、网络连接能力强的操作系统。本专题为大家提供android相关的文章、下载、课程内容,供大家免费下载体验。

1734

2023.08.22

android权限限制怎么解开
android权限限制怎么解开

android权限限制可以使用Root权限、第三方权限管理应用程序、ADB命令和Xposed框架解开。详细介绍:1、Root权限,通过获取Root权限,用户可以解锁所有权限,并对系统进行自定义和修改;2、第三方权限管理应用程序,用户可以轻松地控制和管理应用程序的权限;3、ADB命令,用户可以在设备上执行各种操作,包括解锁权限;4、Xposed框架,用户可以在不修改系统文件的情况下修改应用程序的行为和权限。

1998

2023.09.19

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

36

2026.01.14

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 3.4万人学习

10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号