16. 适配Doze模式的最佳实践:使用WorkManager、避免长连接、合理使用Alarm

说实话,Doze模式刚出来那会儿,我团队里好几个项目都踩了坑。用户反馈说「消息收不到」、「闹钟不响」,搞得我们焦头烂额。后来我花了两周时间,把Google的文档翻了个底朝天,又做了大量实测,才总结出今天要讲的这三条核心实践。

这三条实践,说白了就是:用WorkManager替代后台Service、别让长连接在Doze下硬撑、把Alarm交给系统管理。咱们一条一条拆开聊。

16.1 使用WorkManager:你的后台任务救星

我个人习惯,从Android 6.0开始,所有后台任务都优先考虑WorkManager。为什么?因为它能自动适配Doze模式。

WorkManager会把你的任务交给系统调度。系统会根据Doze状态、电量、网络条件,决定什么时候执行。你想想看,这比你自己写个Service在那死撑,要优雅得多。

核心思路: 把任务描述清楚,剩下的交给系统。

举个例子,假设你要定期上传日志:

// 定义任务
val uploadWork = OneTimeWorkRequestBuilder<LogUploadWorker>()
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresBatteryNotLow(true)
            .build()
    )
    .build()

// 提交任务
WorkManager.getInstance(context).enqueue(uploadWork)

我在项目中遇到过一个问题:有个同事用JobScheduler自己写调度逻辑,结果在Doze下任务被无限延迟。换成WorkManager后,问题迎刃而解。因为WorkManager内部已经处理了Doze的窗口期问题。

小技巧: 如果你的任务需要精确时间,可以用WorkManager的初始延迟。但别指望它像AlarmManager那么准。

16.2 避免长连接:别在Doze下硬撑

长连接,比如WebSocket、MQTT,在Doze模式下是个大坑。系统会限制网络访问,你的连接会被频繁断开重连,反而更耗电。

我曾经有个项目,在Doze下保持长连接,结果用户手机一晚上掉了30%的电。后来我们改成了FCM推送 + 定时心跳,电量消耗降到了5%以下。

那具体怎么做?我建议:

  • 用FCM替代长连接:Google的推送服务,天然适配Doze
  • 如果必须用长连接:在Doze进入前主动断开,等窗口期再重连
  • 别用心跳包硬撑:系统会把它当成普通网络请求,照样受限
注意: 有些开发者会在长连接里加「保活」逻辑,比如互相唤醒。这在Doze下是徒劳的,系统会直接无视你的唤醒请求。

你想想看,Doze的设计初衷就是省电。你非要跟它对着干,结果只能是用户卸载你的App。

16.3 合理使用Alarm:别滥用setExact

AlarmManager在Doze下有两种模式:setExactsetAndAllowWhileIdle。前者在Doze下会被延迟,后者虽然能唤醒设备,但频率受限。

我记得有一次,产品经理要求闹钟必须精确到秒。我跟他解释:Doze下做不到。后来我们妥协了,允许1分钟的误差,用setAndAllowWhileIdle实现。

这里有个表格,帮你快速决策:

场景 推荐API 说明
闹钟、提醒 setAlarmClock 最高优先级,会退出Doze
定时任务 setAndAllowWhileIdle 每分钟最多一次,Doze下可用
非紧急任务 setExact Doze下会被延迟到窗口期
周期性任务 setInexactRepeating 系统会合并触发,省电
避坑指南: 我曾经用setExact做定时同步,结果在Doze下延迟了2小时。后来改成setAndAllowWhileIdle,虽然不准时,但至少能触发。

嗯,这里要注意:setAndAllowWhileIdle 虽然能唤醒设备,但系统会限制频率。如果你频繁调用,系统会忽略你的请求。所以,别用它做心跳。

16.4 知识体系总览

说了这么多,咱们用一张图来总结。这张图展示了Doze适配的三大核心实践,以及它们之间的关系:

Doze模式适配最佳实践 WorkManager 自动适配Doze窗口期 支持约束条件 任务可延迟/合并 替代JobScheduler 避免长连接 Doze下网络受限 改用FCM推送 窗口期再重连 别用心跳保活 合理使用Alarm setAlarmClock最高优先级 setAndAllowWhileIdle受限 setExact会被延迟 setInexactRepeating省电 核心原则 把控制权交给系统,别跟Doze对着干 用WorkManager管理后台任务 长连接和Alarm都要「认怂」

这张图你看懂了吗?三个模块是并列关系,但底层逻辑一致:尊重系统的省电策略。WorkManager帮你自动适配,避免长连接是主动让步,合理使用Alarm是精准控制。

最后说一句:Doze适配没有银弹。但只要你遵循这三条实践,90%的问题都能解决。剩下的10%,嗯,那就要看具体业务场景了。到时候咱们再细聊。

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