7、BLE广播(Advertising):AdvertiseSettings与AdvertiseData的配置、广播回调、兼容性处理(Android 8+限制)、扩展广播(5.0+)

BLE广播,说白了就是让你的手机像个小喇叭一样,不停喊:“我在这儿!我在这儿!我能提供这些服务!” 其他设备听到后,就可以决定要不要连过来。

我个人习惯把广播比作“蓝牙世界的名片”。你想想看,名片上印什么内容、用什么字体、多大尺寸,这些都得精心设计。在Android里,AdvertiseSettingsAdvertiseData就是设计这张名片的工具。

7.1 AdvertiseSettings:广播的“物理层”配置

先说说AdvertiseSettings。它控制的是广播的物理行为,比如功率、模式、通道等。我刚开始做BLE开发时,经常忽略这些参数,结果要么广播距离太短,要么功耗高得吓人。

参数 可选值 说明
AdvertiseMode ADVERTISE_MODE_LOW_POWER / BALANCED / LOW_LATENCY 广播间隔:低功耗约1000ms,平衡约250ms,低延迟约100ms
TxPowerLevel ADVERTISE_TX_POWER_ULTRA_LOW / LOW / MEDIUM / HIGH 发射功率,直接影响广播距离
AdvertiseType ADVERTISE_TYPE_CONNECTABLE / NON_CONNECTABLE / SCANNABLE 是否可连接、可扫描
Timeout 0~180000 ms 广播超时时间,0表示持续广播

核心经验:我在项目中遇到过,有些手机在低功耗模式下广播间隔会漂移,导致扫描端收不到包。后来我统一用BALANCED模式,兼顾功耗和稳定性。

// 我个人习惯这样配置AdvertiseSettings
AdvertiseSettings settings = new AdvertiseSettings.Builder()
    .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
    .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
    .setConnectable(true)
    .setTimeout(0)  // 持续广播
    .build();

7.2 AdvertiseData:广播的“内容层”设计

AdvertiseData决定你的广播包里装什么内容。BLE广播包最大只有31字节,非常珍贵。你得精打细算,把最重要的信息放进去。

广播数据通常包含:

  • Service UUID:告诉别人你支持什么服务
  • Local Name:设备名称(可选,但建议放)
  • Manufacturer Specific Data:厂商自定义数据
  • Service Data:服务相关数据

注意:Android 8.0(API 26)开始,后台应用不能扫描或广播。如果你的App target API 26+,必须在前台服务中执行广播,否则会静默失败。我曾经踩过这个坑,调试了一整天才发现是后台限制。

// 配置广播数据
AdvertiseData data = new AdvertiseData.Builder()
    .addServiceUuid(new ParcelUuid(UUID.fromString("0000FFE0-0000-1000-8000-00805F9B34FB")))
    .setIncludeDeviceName(true)  // 包含设备名称
    .addManufacturerData(0x1234, new byte[]{0x01, 0x02})  // 厂商自定义数据
    .build();

7.3 广播回调:监听广播生命周期

广播不是发出去就完事了。你得监听它的状态:开始成功了吗?失败了吗?被停止了?

private AdvertiseCallback callback = new AdvertiseCallback() {
    @Override
    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
        // 广播启动成功
        // settingsInEffect 是实际生效的设置,可能与传入的不同
    }

    @Override
    public void onStartFailure(int errorCode) {
        // 广播启动失败
        switch (errorCode) {
            case ADVERTISE_FAILED_ALREADY_STARTED:
                // 已经在广播了
                break;
            case ADVERTISE_FAILED_DATA_TOO_LARGE:
                // 广播数据超过31字节
                break;
            case ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
                // 硬件不支持
                break;
            case ADVERTISE_FAILED_INTERNAL_ERROR:
                // 内部错误
                break;
            case ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
                // 广播者太多(Android 8+限制)
                break;
        }
    }
};

避坑指南:我曾经遇到ADVERTISE_FAILED_TOO_MANY_ADVERTISERS错误。原因是系统限制了同时广播的应用数量(通常只有1-2个)。解决方案是:先停止其他广播,或者使用扩展广播(5.0+)。

7.4 兼容性处理:Android 8+的限制

Android 8.0(API 26)引入了很多后台限制,BLE广播也受影响。具体来说:

  • 后台应用不能启动广播:必须在前台服务中执行
  • 广播数量限制:系统限制同时广播的应用数量
  • 扫描限制:后台应用扫描频率被大幅降低

嗯,这里要注意。如果你的App需要长时间广播,必须启动一个前台服务,并显示通知。否则,广播会在几秒后被系统自动停止。

// Android 8+ 必须在前台服务中启动广播
public class BleForegroundService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 创建通知
        Notification notification = new Notification.Builder(this, CHANNEL_ID)
            .setContentTitle("BLE广播中")
            .setContentText("设备正在广播")
            .build();
        startForeground(1, notification);
        
        // 启动广播
        startAdvertising();
        return START_STICKY;
    }
}

7.5 扩展广播:5.0+的新特性

Android 5.0(API 21)引入了BLE扩展广播(Extended Advertising)。它解决了传统广播的两个痛点:

  • 数据容量更大:从31字节扩展到251字节(甚至更多)
  • 广播间隔更灵活:支持更长的广播间隔,降低功耗

为什么会这样?因为传统广播的31字节限制,在物联网场景下根本不够用。比如你要广播温度、湿度、气压三个数据,31字节就捉襟见肘了。

扩展广播的使用方式略有不同:

// 扩展广播需要检查硬件支持
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter.isLeExtendedAdvertisingSupported()) {
    // 使用扩展广播
    AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder()
        .setPrimaryPhy(BluetoothDevice.PHY_LE_1M)
        .setSecondaryPhy(BluetoothDevice.PHY_LE_2M)
        .setInterval(100)  // 100ms间隔
        .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
        .build();
    
    // 扩展广播数据可以更大
    AdvertiseData data = new AdvertiseData.Builder()
        .addServiceUuid(new ParcelUuid(UUID.fromString("0000FFE0-0000-1000-8000-00805F9B34FB")))
        .setIncludeDeviceName(true)
        .addManufacturerData(0x1234, new byte[200])  // 200字节自定义数据
        .build();
} else {
    // 回退到传统广播
    Log.w("BLE", "设备不支持扩展广播,使用传统广播");
}

个人经验:扩展广播虽然好,但兼容性是个问题。很多低端手机(尤其是2018年前的)不支持。我建议的做法是:先检测是否支持扩展广播,如果不支持就回退到传统广播。这样既能享受新特性,又不会让老设备用户无法使用。

7.6 知识体系总览

下面这张图总结了BLE广播的核心知识点,你可以把它当作开发时的快速参考:

BLE广播知识体系 BLE广播 广播配置 AdvertiseSettings AdvertiseData 广播回调 兼容性处理 Android 8+ 后台限制 前台服务要求 广播数量限制 扩展广播 (5.0+) 更大数据容量 (251字节+) 灵活广播间隔 硬件兼容性检测

这张图把广播配置、兼容性处理和扩展广播三个核心模块串起来了。你开发时,可以对照这张图检查自己是否遗漏了某个环节。

最后一个小技巧:调试广播时,我建议用nRF Connect这类工具来扫描,能直观看到广播包的内容。我曾经用它发现自己的广播数据里多了一个空字段,排查了好久才发现是Builder的调用顺序问题。

公众号:蓝海资料掘金营,微信deep3321