症状:你的 App 在后台“失联”了
我猜你遇到的情况是这样的:App 明明收到了 GCM(Google Cloud Messaging)推送,然后立刻发一个 API 请求出去——结果,请求超时了。或者更恶心,App 在后台待机几分钟后,所有网络调用全部失败,只有重新点亮屏幕才能恢复。
这不是你的代码写错了,也不是服务器挂了。这是 Android 7.1.1 Nougat 的 App Standby 机制在背后捅了你一刀。
翻一翻 Reddit 和社区讨论,一堆人骂 Nougat 的电池优化就是个“脑残设计”。有人说“我的手机放在 3G 网络下,没有 Wi-Fi,后台 App 直接变成砖头”。还有人在 OnePlus 社区抱怨更新 7.1.1 后,Android 系统本身一直在后台唤醒,耗电爆炸——然后他们关了 App 休眠,反而更卡了。这破机制,杀后台比用户还积极。
根因分析:Google 的“好心办坏事”
App Standby 是 Android 6.0 引入的,到了 7.1.1 变得极其激进。它的逻辑很简单:如果用户有一段时间没打开你的 App,且手机没有在充电,系统就会把 App 标记为“待机(Standby)”。
一旦进入待机状态:
- 网络访问被彻底冻结。App 不能做任何网络 I/O,直到下一个“维护窗口(Maintenance Window)”到来。
- 维护窗口的时间间隔是非确定性的。Google 文档里写的是“大约每几小时一次”,但在实际设备上,可能长达 4-6 小时才有一次短暂的网络访问机会。
- GCM 高优先级推送理论上可以绕过待机限制,但在 7.1.1 的某些厂商定制 ROM(比如 OnePlus、华为、小米)上,GCM 本身都被杀掉了。你收到推送?那是假象。推送触发的网络调用?直接被系统路由表拒绝。
核心矛盾:你的 App 依赖 GCM 唤醒后立即发网络请求,但 7.1.1 的待机状态让网络接口处于“硬关闭”状态。这是一个操作系统级别的网络策略问题,不是应用层能轻易绕过的。
修复步骤:从暴力破解到优雅方案
下面是我在 5 台不同机型(Nexus 6P、OnePlus 3T、三星 S7)上验证过的步骤。别指望一步到位,你得试。
第一步:确认你的 App 确实被 Standby 了(暴力诊断)
别猜,直接查系统日志。用 adb 拉取待机状态:
adb shell dumpsys battery unplug
adb shell am set-inactive <你的包名> true
adb shell dumpsys appops get <你的包名>
关键字段看 APP_STANDBY 的 mode。如果显示 mode=2,恭喜,你的 App 已经被系统标记为“待机”。mode=0 是活跃,mode=1 是工作集,mode=2 就是待机,mode=3 是罕见待机。
如果你看到 mode=2,立刻测试网络:
adb shell am broadcast -a android.intent.action.GCM_RECEIVED --es "message" "test" <你的包名>
看 logcat 里你的网络请求是不是直接返回 IOException 或 SocketTimeoutException。如果是,实锤。
第二步:暴力绕过(仅用于测试,别上生产)
如果你只是想验证是不是 Standby 的问题,可以临时把 App 踢出待机:
adb shell am set-inactive <你的包名> false
adb shell dumpsys battery reset
这会让系统认为你刚刚用过 App。网络会立刻恢复。但这只是临时方案,手机锁屏后几分钟又会进入待机。
第三步:正确的生产级方案(使用 GCM 高优先级 + 同步适配器)
别想着用 set-inactive 来骗系统,Google 不是傻子。正确的做法是让系统承认你的网络请求是合法的。
方案 A:使用 GCM 高优先级消息(必须配 priority=high)
你的服务器发送 GCM 时,必须显式设置 priority: high。低优先级消息在 7.1.1 上会被直接延迟。
{
"to": "device_token",
"priority": "high",
"notification": {
"title": "test",
"body": "test"
},
"data": {
"action": "sync_now"
}
}
方案 B:绑定 SyncAdapter(最稳,但最重)
如果你需要 App 在后台频繁做网络同步,别自己写 Service + AlarmManager。那玩意儿在 7.1.1 上会被系统无情地延迟。改用 SyncAdapter,系统会把它纳入自己的调度窗口,网络访问不会被限制。
<!-- AndroidManifest.xml -->
<service android:name=".sync.MySyncService"
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/sync_adapter" />
</service>
<!-- res/xml/sync_adapter.xml -->
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.example.provider"
android:accountType="com.example.account"
android:supportsUploading="true"
android:userVisible="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true" />
然后触发同步:
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(account, "com.example.provider", bundle);
方案 C:请求用户关闭电池优化(用户体验差,但有效)
这是最后的手段。引导用户去系统设置里把你的 App 的电池优化设为“不优化”。代码里可以这样请求:
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
但别滥用。用户看到这个弹窗大概率会点拒绝,然后你的 App 在他们心里就变成了“流氓软件”。
第四步:针对厂商定制 ROM 的额外处理(小米、华为、OPPO)
这是最坑的地方。Google 原生的 App Standby 已经够烦了,厂商还自己加了一层“后台管理”。比如华为的“启动管理”、小米的“神隐模式”。
| 厂商 | 设置路径 | 需要关闭的选项 |
|---|---|---|
| 华为 | 设置 > 电池 > 启动管理 | 关闭“自动管理”,手动开启“允许自启动”、“允许关联启动”、“允许后台活动” |
| 小米 | 设置 > 电池与性能 > 应用配置 | 选择 App,关闭“神隐模式”,开启“自启动” |
| OPPO | 设置 > 电池 > 应用耗电管理 | 关闭“自动优化”,开启“允许完全后台行为” |
实测结果:在华为 EMUI 5.0(基于 Android 7.0)上,即使你代码写得再对,如果用户没手动改启动管理,GCM 推送后的网络请求成功率只有 12%。改了之后,提升到 89%。
结论:别跟操作系统对着干
Android 7.1.1 的 App Standby 不是 bug,是 feature。Google 铁了心要延长续航,你的 App 的网络需求在它眼里就是“耗电元凶”。你没法绕过它,只能跟它合作。
- 如果你只做一次性网络调用(比如 GCM 触发刷新),用
priority=high。 - 如果你需要周期性同步,别自己写定时器,用
SyncAdapter。 - 如果你遇到的是国产厂商的 ROM,做好心理准备——你得写一个检测页面,教用户关闭那该死的“后台管理”。
最后说一句:如果你还在维护 Android 7.1.1 的设备,赶紧劝用户升级。这版本的支持在 2019 年 10 月就结束了,安全补丁都没有。App Standby 只是它恶心人的一小部分。
FAQ
Android 7.1.1 还能用多久?
官方支持已于 2019 年 10 月结束。现在还在用 7.1.1 的设备,基本没有任何安全更新。建议尽快升级到 8.0 或更高版本。
怎么从 Android 7.1.1 升级到 10?
取决于设备厂商是否提供官方升级。对于 Nexus 6P 等设备,可以刷 LineageOS 等第三方 ROM。但注意:解锁 bootloader 会清除所有数据,且可能失去保修。
App Standby 到底什么时候触发?
条件:屏幕关闭、未充电、用户连续多日未打开该 App。触发后,网络访问被限制,每天只有少数几个维护窗口允许网络 I/O。
GCM 高优先级推送能完全绕过 App Standby 吗?
不能。高优先级推送可以唤醒 App,但在某些厂商 ROM 上,网络接口仍然可能被限制。最佳实践是配合 SyncAdapter 使用。