8、网络评分与切换:WifiNetworkScore、网络选择逻辑、自动切换触发条件
各位同学,今天我们来聊聊Wi-Fi服务里一个特别有意思的话题——网络评分与切换。
说实话,我刚接触这部分代码时,第一反应是:不就是信号弱了换个网吗?有什么好讲的?
后来被现实狠狠教育了一顿。有一次我在做某个定制项目,用户反馈手机在会议室里频繁断连。我查了半天,发现不是信号问题,而是评分机制把5GHz的弱信号评得比2.4GHz的强信号还高,导致手机死守着5GHz不放。嗯,从那以后,我再也不敢小看这个评分模块了。
8.1 网络评分:WifiNetworkScore 的底层逻辑
Wi-Fi网络评分,说白了就是给每个可见的网络打个分。分数高的优先连,分数低的靠边站。
Android里这个评分机制藏在 WifiNetworkScore 这个类里。它不是一个简单的RSSI映射,而是一个综合评估模型。
核心评分维度:
- 信号强度(RSSI):基础分,-50dBm 比 -80dBm 高,这是常识
- 频段偏好:5GHz 通常比 2.4GHz 加分,因为干扰少、速度快
- 安全类型:WPA3 比 WEP 高,开放网络最低
- 历史连接记录:之前连上后掉线过?扣分。连上后很稳定?加分
- 当前负载:AP 接入设备太多?分数会降
我个人习惯把评分想象成一个「面试官」。面试官会看学历(信号强度)、工作经验(历史记录)、性格(安全类型)、甚至颜值(频段)。最后给个综合分,决定要不要录用。
来看一段核心代码,这是 WifiNetworkScore 的简化版:
// 来自 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNetworkScore.java
public class WifiNetworkScore {
private int mRssiScore; // 信号分
private int mBandScore; // 频段分
private int mSecurityScore; // 安全分
private int mHistoryScore; // 历史分
private int mLoadScore; // 负载分
public int calculateTotalScore() {
int total = 0;
total += mRssiScore * 0.4; // 信号权重 40%
total += mBandScore * 0.25; // 频段权重 25%
total += mSecurityScore * 0.2; // 安全权重 20%
total += mHistoryScore * 0.1; // 历史权重 10%
total += mLoadScore * 0.05; // 负载权重 5%
return total;
}
}
你可能会问:为什么信号权重占40%?其实这个比例不是固定的。不同厂商、不同Android版本都会调。我在某款平板项目里,因为用户经常在弱信号区域移动,就把信号权重提到了60%。效果立竿见影,切换更积极了。
避坑指南:我曾经遇到过一个坑——评分计算时没有考虑「同频干扰」。在密集部署的商场里,2.4GHz虽然信号满格,但实际吞吐量极低。后来我们在评分里加了一个「信道利用率」因子,才解决了这个问题。
8.2 网络选择逻辑:谁才是最优解?
评分算完了,接下来就是选谁的问题。Android的网络选择逻辑,我把它总结为三步走:
- 候选列表构建:把所有可见的、已保存的、安全的网络列出来
- 评分排序:按 WifiNetworkScore 从高到低排
- 择优录取:选分数最高的那个,发起连接
但这里有个细节——不是每次扫描都切换。Android有个「切换阈值」机制。简单说,新网络的分数必须比当前网络高出一定值,才会触发切换。这个阈值默认是10分(具体看厂商配置)。
为什么要设阈值?你想想看,如果每次信号波动都切换,手机就会变成「墙头草」,频繁断连重连,用户体验极差。
我参与过一个车载项目,车在移动中,Wi-Fi信号变化极快。我们不得不把阈值从10分降到5分,同时缩短扫描间隔。嗯,这里要注意,降低阈值会带来更频繁的切换,但车载场景下,用户更在意「能连上」,而不是「稳定」。场景决定策略。
网络选择的核心流程(伪代码):
// 网络选择逻辑简化版
List<ScanResult> candidates = getVisibleNetworks();
candidates = filterBySecurity(candidates); // 过滤不安全网络
candidates = filterByHistory(candidates); // 过滤黑名单网络
// 评分排序
candidates.sort((a, b) -> {
int scoreA = WifiNetworkScore.calculate(a);
int scoreB = WifiNetworkScore.calculate(b);
return scoreB - scoreA; // 降序
});
// 择优录取
ScanResult best = candidates.get(0);
if (best.score > currentNetwork.score + THRESHOLD) {
connectTo(best);
}
这段代码看起来简单,但实际工程里要考虑的边界情况很多。比如:候选列表为空怎么办?当前网络分数为0怎么办?所有网络分数都低于阈值怎么办?
我个人习惯在候选列表为空时,保持当前连接不变,同时启动一个「紧急扫描」——缩短扫描周期,直到找到可用网络。
8.3 自动切换触发条件:什么时候该动?
自动切换不是随时发生的。Android定义了几个明确的触发条件:
| 触发条件 | 说明 | 典型场景 |
|---|---|---|
| 信号低于阈值 | 当前网络 RSSI 低于 -85dBm | 走到信号死角 |
| 发现更优网络 | 新网络评分比当前高 10 分以上 | 进入另一个AP覆盖区 |
| 连接质量下降 | 丢包率超过 20% 或延迟超过 500ms | AP 负载过高 |
| 用户主动触发 | 用户在设置里点击「切换网络」 | 手动选择 |
这里我想重点说说「连接质量下降」这个条件。很多开发者只关注信号强度,忽略了质量。我在一个视频会议项目里遇到过:信号满格,但视频卡成PPT。查了半天,发现是AP上接了太多设备,导致每个设备分到的带宽极低。
从那以后,我在评分模型里加入了「吞吐量探测」——定期发一个小包,测一下实际速度。如果速度低于预期,即使信号满格,也会触发切换。
注意:吞吐量探测不能太频繁。我见过有人每5秒测一次,结果手机CPU占用飙升,电池狂掉。建议间隔30秒以上,且只在信号良好但用户体验差时才触发。
还有一个容易被忽略的触发条件——「漫游」。在企业级网络里,多个AP组成一个ESSID。当手机从一个AP移动到另一个AP时,需要触发「漫游切换」。这个切换和普通切换不同,它不改变SSID,只改变BSSID。
Android的漫游逻辑在 WifiConnectivityManager 里。它会维护一个「候选BSSID列表」,按评分排序。当当前BSSID的评分低于某个阈值时,自动切换到列表里评分最高的BSSID。
我建议你在做漫游优化时,重点关注「切换时延」。从检测到信号变差,到完成新AP关联,这个时间越短越好。理想情况下,应该控制在200ms以内,否则用户会感觉到卡顿。
8.4 知识体系总览
为了让你更直观地理解整个网络评分与切换的体系,我画了一张图:
这张图从左到右展示了整个流程:输入层收集数据,评分引擎计算分数,选择逻辑决定是否切换,触发条件判断何时执行。每个环节都有对应的代码实现和配置参数。
最后说一句:网络评分与切换,看似是个小功能,但直接影响用户对「手机好不好用」的第一印象。我见过太多项目,因为切换逻辑没做好,被用户骂「Wi-Fi老是断」。所以,花时间把这块吃透,绝对值。
公众号:蓝海资料掘金营,微信deep3321