运维笔记

Android 7.1.1 Nougat App Standby 网络锁死?手撕后台限制与 GCM 失联修复方案

Android Nougat 网络调试

症状:你的 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_STANDBYmode。如果显示 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 里你的网络请求是不是直接返回 IOExceptionSocketTimeoutException。如果是,实锤。

第二步:暴力绕过(仅用于测试,别上生产)

如果你只是想验证是不是 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 使用。