低功耗优化策略:扫描功耗控制、连接间隔调整、Doze模式适配、后台限制处理

做BLE开发,说白了就是在跟电池较劲。

我刚开始做第一个BLE项目时,满脑子想的都是「怎么把数据传上去」。结果产品上线后,用户反馈手机半天就没电了。嗯,那会儿我才意识到——低功耗不是可选项,是必选项

这一章,我把这几年踩过的坑、总结的经验,全部分享给你。咱们从四个维度来聊:扫描功耗、连接间隔、Doze模式、后台限制。

核心观点:BLE低功耗优化的本质,是在「响应速度」和「功耗」之间找平衡。你不可能两头都占,必须做取舍。

BLE 低功耗优化四大策略 低功耗 优化策略 扫描功耗控制 扫描窗口/间隔 连接间隔调整 Connection Interval Doze模式适配 省电模式兼容 后台限制处理 Android 8+ 限制 四大策略相互影响,需要根据业务场景综合权衡

一、扫描功耗控制:别让手机一直「竖着耳朵」

扫描,是BLE最耗电的操作之一。你想想看,手机要一直监听广播信道,射频模块不能休息,这电量哗哗地掉。

我个人习惯的做法是:

  • 不要持续扫描。扫描一段时间,停一会儿,再扫。这叫「间歇扫描」。
  • 控制扫描窗口。扫描窗口越大,发现设备越快,但越耗电。
  • 扫描间隔别太短。间隔太短,相当于一直在扫,没区别。

经验之谈:我在项目中遇到过这样的情况——扫描窗口设成100ms,间隔200ms,结果设备发现速度还行,但电量消耗比预期高了30%。后来改成窗口50ms、间隔400ms,发现速度只慢了不到1秒,电量却省了一半。

来看看代码怎么写:

// 低功耗扫描配置示例
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();

ScanSettings settings = new ScanSettings.Builder()
    .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)  // 低功耗模式
    .setReportDelay(1000)  // 批量上报,减少回调次数
    .build();

// 配合扫描过滤器,只扫需要的设备
List<ScanFilter> filters = new ArrayList<>();
filters.add(new ScanFilter.Builder()
    .setServiceUuid(ParcelUuid.fromString(DEVICE_UUID))
    .build());

scanner.startScan(filters, settings, scanCallback);

这里有个细节:setReportDelay设成1000ms,意思是1秒内扫描到的结果一次性回调。这样能减少CPU唤醒次数,省电效果很明显。

注意:如果你需要快速发现设备(比如用户主动点击「搜索」按钮),可以临时切换到SCAN_MODE_LOW_LATENCY。发现后立刻切回低功耗模式。别一直用高延迟模式,用户体验会变差。

二、连接间隔调整:别让手机和蓝牙设备「聊太勤」

连接间隔,就是两个设备之间交换数据的频率。间隔越短,数据越实时,但功耗越高。

Android BLE默认的连接间隔范围是7.5ms到4000ms。你可以在连接时请求一个合适的间隔。

连接间隔 适用场景 功耗水平
7.5ms - 15ms 音频、实时控制
30ms - 50ms 传感器数据采集 中等
100ms - 400ms 状态同步、低频率数据

我建议的做法:

  • 如果只是传温度、电量这种低频数据,连接间隔设到200ms以上完全够用。
  • 如果是心率这种需要实时性的,可以设到30ms左右。
  • 千万别设成7.5ms除非你真的需要——那相当于每7.5毫秒唤醒一次,电池扛不住。
// 请求连接参数示例
BluetoothGatt gatt = device.connectGatt(context, false, gattCallback);

// 连接成功后,请求调整连接间隔
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER);

// 可选参数:
// CONNECTION_PRIORITY_HIGH - 低延迟(7.5ms)
// CONNECTION_PRIORITY_BALANCED - 平衡(30ms左右)
// CONNECTION_PRIORITY_LOW_POWER - 低功耗(100ms以上)

避坑指南:我曾经在一个项目中,把连接间隔设成了7.5ms,结果手机发热严重,半天就没电了。后来发现其实数据量很小,改成100ms后,功耗降了70%,数据也没丢。所以,别盲目追求低延迟,够用就行。

三、Doze模式适配:别让系统「休眠」把你的蓝牙干掉

Android 6.0引入了Doze模式。手机静止一段时间后,系统会限制网络和CPU活动。你的BLE连接可能被挂起,甚至断开。

为什么会这样?因为Doze模式下,系统会推迟应用的唤醒锁和网络请求。BLE虽然属于硬件层面,但回调仍然需要CPU来处理。

适配方案:

  • 使用前台服务。前台服务有更高的优先级,不容易被Doze限制。
  • 申请忽略电池优化。但要注意,这个权限需要用户手动授权,不能静默申请。
  • 利用高精度定时器。Doze模式下,普通定时器会被合并,但BLE的回调不受影响。
// 前台服务示例(部分代码)
public class BleForegroundService extends Service {
    private static final int NOTIFICATION_ID = 1001;
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 创建通知,让系统知道这是前台服务
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("蓝牙设备已连接")
            .setContentText("正在后台运行")
            .setSmallIcon(R.drawable.ic_bluetooth)
            .build();
        
        startForeground(NOTIFICATION_ID, notification);
        return START_STICKY;
    }
}

小技巧:如果你的应用不需要持续连接,可以在Doze进入前主动断开BLE连接,等用户唤醒手机后再重新连接。这样反而更省电。我在一个智能家居项目里就是这么做的,用户反馈续航明显提升。

四、后台限制处理:Android 8+ 的「紧箍咒」

Android 8.0开始,系统对后台应用的限制越来越严。后台应用不能随意启动扫描,也不能频繁获取位置信息。

具体限制包括:

  • 后台应用扫描BLE设备,结果会被延迟上报。
  • 后台应用不能随意启动或绑定服务。
  • 后台应用的位置权限会被限制。

应对策略:

  1. 使用前台服务。这是最稳妥的方式,系统会把你的应用视为「活跃」状态。
  2. 申请后台位置权限。BLE扫描需要位置权限,后台扫描需要ACCESS_BACKGROUND_LOCATION
  3. 利用JobScheduler。在合适的时机(比如充电时、WiFi连接时)执行扫描任务。
// 后台位置权限申请(Android 10+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    requestPermissions(new String[]{
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION
    }, REQUEST_CODE);
}

// 使用JobScheduler安排后台扫描
ComponentName component = new ComponentName(this, BleScanJob.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, component)
    .setMinimumLatency(5 * 60 * 1000)  // 至少5分钟后执行
    .setOverrideDeadline(15 * 60 * 1000) // 最多15分钟后执行
    .setRequiresCharging(false)
    .build();

JobScheduler scheduler = getSystemService(JobScheduler.class);
scheduler.schedule(jobInfo);

重要提醒:不要试图绕过系统限制。有些开发者用「保活」手段,比如双进程守护、1像素Activity等。这些方法在Android 10+基本失效,而且会被应用商店下架。老老实实用官方API,才是正道。

写在最后

低功耗优化,说白了就是一场「权衡游戏」。你要在功耗、响应速度、系统兼容性之间找到平衡点。

我个人总结了几条原则:

  • 能不扫就不扫。扫描是最耗电的,能缓存就缓存。
  • 能慢就慢。连接间隔、扫描间隔,够用就行,别追求极致。
  • 尊重系统。Doze和后台限制是保护用户电池的,别硬刚。
  • 测试要全面。不同厂商的手机,功耗表现差异很大。我见过某款手机在Doze模式下BLE直接断连,而另一款却正常。

嗯,这一章的内容就到这里。记住,低功耗不是一蹴而就的,需要在真实场景中反复调优。你可以在开发阶段用Battery Historian工具分析功耗,找到瓶颈再优化。


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