第二十章:Telephony 协同:蜂窝与 Wi-Fi 切换、IMS over Wi-Fi、数据流量管理

说实话,做 Android 系统开发这么多年,Wi-Fi 和蜂窝网络之间的协同,一直是个让人又爱又恨的话题。爱的是,一旦调好了,用户体验会非常丝滑;恨的是,这里面的坑实在太多了。我印象最深的一次,是某款手机在电梯里频繁切换网络,用户投诉说「微信发不出去」。查了半天,发现是 Wi-Fi 信号弱但没断开,蜂窝网络又不敢抢,结果两边都卡着。

这一章,我们就来聊聊 Wi-Fi 和 Telephony 是怎么协同工作的。说白了,就是让手机知道什么时候用 Wi-Fi,什么时候切到 4G/5G,以及怎么让 VoWiFi(Wi-Fi 通话)跑得稳。

20.1 蜂窝与 Wi-Fi 的切换策略

切换策略,核心就一句话:谁信号好,谁上。但实际做起来,远没那么简单。

Android 系统里,Wi-Fi 和蜂窝的切换主要由 ConnectivityServiceTelephonyManager 配合完成。系统会维护一个「网络评分」机制,给每个可用网络打分。分数高的,就优先使用。

核心评分因素:

  • Wi-Fi RSSI(信号强度)
  • 蜂窝网络信号强度(RSRP/RSRQ)
  • 网络延迟(ping 测试)
  • 数据吞吐量(实际测速)
  • 用户偏好设置(比如「Wi-Fi 优先」)

我遇到过一个问题:某款手机在 Wi-Fi 信号很弱时,系统依然坚持用 Wi-Fi,因为 Wi-Fi 的初始评分比蜂窝高。结果用户刷抖音一直卡。后来我们加了一个「信号抖动检测」,如果 Wi-Fi 信号在短时间内波动超过 10dBm,就主动降权,让蜂窝接管。

代码层面,切换逻辑主要在 NetworkAgent 里。每个网络连接都有一个对应的 Agent,系统通过它们来管理网络状态。

// 伪代码:网络评分调整
if (wifiRssi < -80dBm) {
    wifiScore -= 20;  // 信号太差,扣分
}
if (cellSignal > -100dBm) {
    cellScore += 10;  // 蜂窝信号还行,加分
}
if (wifiScore < cellScore) {
    // 触发切换
    requestNetworkSwitch(CELLULAR);
}

小技巧: 我个人习惯在调试时,用 adb shell dumpsys connectivity 查看当前网络评分。能看到每个网络的详细分数,方便定位问题。

20.2 IMS over Wi-Fi(VoWiFi)

VoWiFi,也叫 Wi-Fi 通话。说白了,就是你的手机通过 Wi-Fi 网络打电话,而不是用蜂窝基站。这个功能在信号差的地方特别有用,比如地下室、电梯里。

IMS(IP Multimedia Subsystem)是核心。它负责把语音通话封装成 IP 数据包,通过 Wi-Fi 发送出去。Android 系统里,IMS 服务由 ImsService 管理,它和 Telephony 框架紧密集成。

我记得有一次,某款手机在连接公司 Wi-Fi 时,VoWiFi 注册总是失败。查日志发现,是 SIP 信令被公司防火墙拦截了。后来我们加了一个「代理检测」机制,如果发现 SIP 信令不通,就自动切换到蜂窝网络进行 IMS 注册。

VoWiFi 的注册流程大致如下:

  1. 手机连接 Wi-Fi 网络
  2. 系统检测 Wi-Fi 是否可用(网络连通性、延迟等)
  3. IMS 服务发起 SIP 注册请求
  4. 运营商网络验证用户身份
  5. 注册成功,手机显示「Wi-Fi 通话」图标

注意: 不是所有 Wi-Fi 网络都支持 VoWiFi。有些公共 Wi-Fi 会封锁 SIP 端口(5060/5061),导致注册失败。我曾经在酒店调试时,就遇到过这种情况。解决方案是让用户切换到蜂窝网络,或者使用 VPN。

代码层面,IMS 注册状态可以通过 ImsManager 获取:

// 检查 VoWiFi 是否注册成功
ImsManager imsManager = ImsManager.getInstance(context, subscriptionId);
if (imsManager.isWifiCallingAvailable()) {
    // VoWiFi 可用
    Log.d("VoWiFi", "Wi-Fi 通话已注册");
} else {
    // 注册失败,检查原因
    int reason = imsManager.getWifiCallingRegistrationError();
    Log.e("VoWiFi", "注册失败,错误码: " + reason);
}

20.3 数据流量管理

数据流量管理,说白了就是让手机知道「什么时候用 Wi-Fi 流量,什么时候用蜂窝流量」。Android 系统提供了 NetworkPolicyManager 来管理这些策略。

核心概念是「计量网络」(Metered Network)。Wi-Fi 通常是非计量的,蜂窝网络是计量的。系统会根据这个属性来决定是否允许后台数据同步、应用更新等操作。

数据流量管理的关键点:

  • 后台数据限制: 在蜂窝网络下,限制后台应用的数据使用
  • 数据节省模式: 系统级的数据压缩和限制
  • 应用级策略: 每个应用可以单独设置是否允许使用蜂窝数据
  • Wi-Fi 与蜂窝切换时的数据流保持: 避免切换时断流

我遇到过一个问题:用户在 Wi-Fi 和蜂窝之间切换时,正在下载的文件会中断。这是因为 TCP 连接在切换时没有平滑迁移。后来我们引入了 Multipath TCP 或者 MPTCP 的解决方案,让数据流在切换时保持不断。

代码层面,监听网络切换可以用 ConnectivityManager.NetworkCallback

ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        // 新网络可用
        Log.d("Network", "新网络连接: " + network);
    }

    @Override
    public void onLost(Network network) {
        // 网络断开
        Log.d("Network", "网络断开: " + network);
    }

    @Override
    public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) {
        // 网络能力变化(比如从 Wi-Fi 切换到蜂窝)
        if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
            Log.d("Network", "当前使用 Wi-Fi");
        } else if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
            Log.d("Network", "当前使用蜂窝网络");
        }
    }
};

connectivityManager.registerNetworkCallback(builder.build(), callback);

避坑指南: 我曾经在调试时发现,某些第三方应用在监听网络切换时,会频繁唤醒 CPU,导致耗电增加。建议在 onCapabilitiesChanged 回调中,不要做耗时操作,只做状态记录。真正的业务逻辑,放到工作线程里处理。

20.4 知识体系总览

为了让你更直观地理解这一章的内容,我画了一张流程图。它展示了 Wi-Fi 和蜂窝协同的核心逻辑。

Wi-Fi 与蜂窝协同核心流程 设备启动 / 网络变化 Wi-Fi 可用? Wi-Fi 网络评分 (信号/延迟/吞吐量) 蜂窝网络评分 (RSRP/RSRQ/延迟) 比较 Wi-Fi 与蜂窝评分 Wi-Fi 评分更高? (且满足 VoWiFi 条件) 使用 Wi-Fi (VoWiFi 优先) 使用蜂窝网络 (VoLTE 优先)

这张图展示了从网络检测到最终决策的完整流程。核心就是评分比较。Wi-Fi 和蜂窝谁得分高,谁就接管数据通道。VoWiFi 和 VoLTE 的优先级也在这个流程里体现。

20.5 实战中的坑与对策

做 Wi-Fi 和 Telephony 协同开发,有几个坑是绕不开的。我列出来,你以后遇到了可以直接翻出来看。

常见问题 原因 解决方案
Wi-Fi 信号弱但不切换 评分机制未考虑信号抖动 加入信号抖动检测,动态调整评分
VoWiFi 注册失败 SIP 端口被防火墙封锁 添加代理检测,自动回退到蜂窝
切换时数据断流 TCP 连接未平滑迁移 使用 MPTCP 或应用层重连机制
后台数据耗电 应用频繁监听网络变化 限制回调中的耗时操作,使用工作线程

重要提醒: 千万不要在 onAvailableonLost 回调里直接发起网络请求。我曾经见过一个同事,在回调里直接做 DNS 解析,结果导致 UI 线程卡死。正确的做法是:记录状态,然后在工作线程里处理。

好了,这一章的内容就到这里。Wi-Fi 和 Telephony 的协同,说白了就是一场「信号与策略」的博弈。理解了评分机制和 IMS 注册流程,大部分问题都能迎刃而解。


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