蓝牙优化:BLE连接优化、MTU调整、数据分包策略、扫描参数配置

蓝牙低功耗(BLE)优化,说实话,是Android性能优化里最容易被忽视的一环。很多人觉得蓝牙嘛,连上就能用,有什么好调的?但我在项目中踩过不少坑,有一次设备在商场里死活连不上,最后发现是扫描参数没配好——嗯,从那以后我再也不敢小看BLE优化了。

今天咱们就聊聊BLE优化的四个核心方向:连接优化、MTU调整、数据分包策略、扫描参数配置。每个点我都会结合实战经验来讲。

一、BLE连接优化:别让连接成为瓶颈

BLE连接,说白了就是设备和手机之间建立通信链路的过程。这个过程如果慢了,用户体验会非常差。我见过一个项目,连接耗时超过3秒,用户都以为设备坏了。

连接优化的核心在于几个参数:

  • 连接间隔(Connection Interval):设备与手机之间交换数据的频率。间隔越小,数据交换越频繁,但功耗也越高。
  • 延迟(Slave Latency):设备可以跳过多少个连接事件而不发送数据。适当增加延迟可以降低功耗。
  • 监督超时(Supervision Timeout):如果超过这个时间没有收到数据,连接会被认为断开。

我个人习惯的做法是:

// 设置连接参数
BluetoothGatt gatt = device.connectGatt(context, false, gattCallback);
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
gatt.requestMtu(512); // 后面会讲MTU

这里要注意,requestConnectionPriority 有三个级别:

优先级 连接间隔 适用场景
CONNECTION_PRIORITY_LOW 100ms - 200ms 低功耗、非实时数据
CONNECTION_PRIORITY_BALANCED 30ms - 50ms 默认、平衡功耗与性能
CONNECTION_PRIORITY_HIGH 11.25ms - 15ms 高实时性、大数据量传输
注意:连接优先级只是请求,最终由蓝牙协议栈和远端设备协商决定。不是所有设备都支持高优先级。

二、MTU调整:别让数据卡在门口

MTU(Maximum Transmission Unit)是蓝牙连接中一次能传输的最大数据包大小。默认MTU是23字节,其中3字节是协议头,实际有效数据只有20字节。你想想看,如果一次要传几百字节的数据,得拆成多少个包?

我在项目中遇到过一个问题:传输一张图片,默认MTU下要拆成50多个包,传输速度慢得让人抓狂。后来调整MTU到512,速度提升了近10倍。

MTU调整的流程是这样的:

// 请求MTU
gatt.requestMtu(512);

// 在回调中处理
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        Log.d("BLE", "MTU changed to: " + mtu);
        // 根据实际MTU调整分包策略
    }
}

MTU调整的注意事项:

  • 不是越大越好:MTU越大,单次传输的数据越多,但出错重传的成本也越高。
  • 设备支持上限:有些老旧设备只支持23字节MTU,请求大MTU会失败。
  • Android版本差异:Android 5.0以上才支持MTU协商,低版本需要自己实现。
我的建议:先请求一个较大的MTU(比如512),如果失败再回退到默认值。这样既能保证性能,又能兼容老旧设备。

三、数据分包策略:大块数据怎么切?

当数据量超过MTU时,就需要分包发送。分包策略的好坏,直接影响传输效率和可靠性。

分包策略的核心原则:

  1. 按MTU大小分包:每个包的有效数据不超过MTU-3(协议头开销)。
  2. 添加序列号:接收方需要知道包的顺序,才能正确重组。
  3. 超时重传:如果某个包丢失,需要重传。

我常用的分包代码:

public class BlePacket {
    private static final int HEADER_SIZE = 3; // 序列号(2字节) + 标志位(1字节)
    
    public static List<byte[]> split(byte[] data, int mtu) {
        List<byte[]> packets = new ArrayList<>();
        int payloadSize = mtu - HEADER_SIZE;
        int totalPackets = (int) Math.ceil(data.length / (double) payloadSize);
        
        for (int i = 0; i < totalPackets; i++) {
            int offset = i * payloadSize;
            int length = Math.min(payloadSize, data.length - offset);
            
            ByteBuffer buffer = ByteBuffer.allocate(length + HEADER_SIZE);
            buffer.putShort((short) i); // 序列号
            buffer.put((byte) (i == totalPackets - 1 ? 1 : 0)); // 结束标志
            buffer.put(data, offset, length);
            
            packets.add(buffer.array());
        }
        return packets;
    }
}

分包策略的避坑指南:

  • 我曾经遇到过一个问题:分包后没有加序列号,结果接收方收到的数据顺序全乱了。从那以后,序列号成了我的标配。
  • 注意包大小:不要刚好填满MTU,留一点余量给协议栈的额外开销。
  • 流控机制:如果发送速度太快,接收方可能来不及处理。可以加一个简单的ACK机制。

四、扫描参数配置:找到设备的关键

BLE扫描是发现设备的第一步。扫描参数配置不好,要么找不到设备,要么功耗爆炸。

扫描参数主要有三个:

  • 扫描模式:低功耗、平衡、高功耗。
  • 扫描间隔:两次扫描之间的时间间隔。
  • 扫描窗口:每次扫描持续的时间。

扫描模式的配置:

// 创建扫描设置
ScanSettings settings = new ScanSettings.Builder()
    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 高功耗模式
    .setReportDelay(0) // 实时上报
    .build();

// 创建扫描过滤器
List<ScanFilter> filters = new ArrayList<>();
filters.add(new ScanFilter.Builder()
    .setServiceUuid(ParcelUuid.fromString("0000XXXX-0000-1000-8000-00805F9B34FB"))
    .build());

// 开始扫描
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(filters, settings, scanCallback);

扫描参数的推荐配置:

场景 扫描模式 扫描间隔(ms) 扫描窗口(ms)
后台扫描 SCAN_MODE_LOW_POWER 2000 500
前台扫描 SCAN_MODE_BALANCED 500 250
快速发现 SCAN_MODE_LOW_LATENCY 100 100
注意:Android 8.0以上对后台扫描有严格限制。如果应用在后台,扫描会被系统限制频率。建议使用前台服务或WorkManager来管理扫描。

五、知识体系总览

为了让你更直观地理解BLE优化的整体结构,我画了一张图:

BLE优化知识体系 连接优化 MTU调整 分包策略 扫描参数 关键参数 连接间隔 Slave延迟 监督超时 调整流程 请求MTU 协商结果 回退策略 核心原则 按MTU分包 序列号 超时重传 配置项 扫描模式 扫描间隔 扫描窗口 优化目标 低延迟 + 高吞吐 + 低功耗 + 高可靠性 四个模块相互影响,需要根据实际场景权衡

这张图把BLE优化的四个核心模块串起来了。你想想看,连接优化决定了通信的基础,MTU调整决定了单次传输的大小,分包策略决定了大数据怎么传,扫描参数决定了设备能不能被找到。四个模块缺一不可。

核心总结:

  • 连接优化:调整连接间隔、延迟、超时,平衡功耗与性能
  • MTU调整:请求更大的MTU,减少分包次数,提升传输效率
  • 分包策略:按MTU分包,加序列号,实现可靠传输
  • 扫描参数:根据场景选择扫描模式,避免功耗浪费

好了,BLE优化就聊到这里。这些经验都是我一个个坑踩出来的,希望能帮你少走弯路。记住,没有万能的配置,只有最适合你场景的方案。

专注资料整理