3、网络请求与评分机制:NetworkRequest 构建、NetworkCapabilities 详解、评分规则定制
好,咱们进入第三章。这一章我打算聊聊车载网络连接里最核心的两个东西——NetworkRequest 和 NetworkCapabilities。说白了,就是「你想要什么样的网络」和「系统给你什么样的网络」之间的匹配游戏。
我在做第一代智能座舱项目时,就吃过这个亏。当时车机连上了 Wi-Fi,但网速慢得像蜗牛,导航地图死活刷不出来。后来一查,原来是系统默认选了信号最强的 Wi-Fi,压根没考虑带宽和延迟。嗯,从那以后,我就开始深入研究这套评分机制了。
3.1 NetworkRequest 构建:你想要什么样的网络?
先说说 NetworkRequest。它就像你给系统下的一份「网络订单」。你告诉系统:我需要一个网络,它要满足这些条件。系统收到订单后,会去匹配当前可用的网络。
构建一个 NetworkRequest 其实很简单,核心就是 NetworkRequest.Builder。我个人习惯这样写:
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET) // 必须能上网
.addCapability(NET_CAPABILITY_NOT_RESTRICTED) // 不能是受限网络
.addTransportType(TRANSPORT_WIFI) // 优先用 Wi-Fi
.addTransportType(TRANSPORT_CELLULAR) // 蜂窝网络也行
.setNetworkSpecifier("carrier_1") // 指定运营商(可选)
.build();
这里有几个关键点,我踩过坑,得提醒你:
- addCapability 是「必须满足」的条件,不是「希望满足」。你加了
NET_CAPABILITY_INTERNET,系统就只会给你能上网的网络。 - addTransportType 是「候选类型」,不是「排他性」的。你加了 Wi-Fi 和蜂窝,系统会在两者之间选一个评分高的。
- setNetworkSpecifier 这个我很少用,除非你要绑定某个特定的网络接口。比如车机同时插了两张 SIM 卡,你想指定用哪张。
addCapability(NET_CAPABILITY_VALIDATED)。这个能力表示网络已经通过了系统的连通性验证。我一般都会加上它,避免拿到一个「假连接」。
3.2 NetworkCapabilities 详解:系统给你什么样的网络?
NetworkCapabilities 是系统的「网络能力报告」。当你发起请求后,系统会回调给你一个 NetworkCapabilities 对象,告诉你当前匹配的网络长什么样。
它里面包含的信息非常丰富,我列个表给你看:
| 能力类型 | 常用常量 | 说明 |
|---|---|---|
| 传输类型 | TRANSPORT_WIFI, TRANSPORT_CELLULAR, TRANSPORT_ETHERNET, TRANSPORT_BLUETOOTH | 网络底层用的是哪种物理链路 |
| 网络能力 | NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_VALIDATED, NET_CAPABILITY_NOT_METERED | 网络具备哪些功能特性 |
| 链路属性 | LinkProperties(包含 MTU、DNS、路由等) | 网络链路的详细参数 |
| 信号强度 | getSignalStrength() | 返回 0-4 的整数,4 表示最强 |
| 带宽估计 | getLinkDownstreamBandwidthKbps(), getLinkUpstreamBandwidthKbps() | 系统估算的下行/上行带宽(kbps) |
你想想看,有了这些信息,你就能做很多事了。比如判断当前网络是不是「不限流量」的:
boolean isUnmetered = capabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
if (isUnmetered) {
// 可以放心下载离线地图
} else {
// 提醒用户注意流量
}
我记得有一次,测试反馈说车机在连接手机热点时,大文件下载总是中断。我查了半天,发现是热点网络被标记为 NET_CAPABILITY_NOT_METERED,但实际带宽只有 2Mbps。系统以为网络很好,拼命下载,结果缓冲区溢出。从那以后,我养成了一个习惯:不仅要看能力,还要看带宽。
3.3 评分规则定制:系统如何选择最佳网络?
好,重头戏来了。系统同时有 Wi-Fi、蜂窝、以太网可用时,它选哪个?答案是:评分。
Android 的 ConnectivityService 内部有一套默认的评分规则。每个网络都有一个「分数」,分数高的优先使用。默认规则大致是这样的:
- 以太网:100 分(最高优先级)
- Wi-Fi:60 分起,信号越好分数越高
- 蜂窝:50 分起,根据信号强度和接入技术(4G/5G)调整
- 蓝牙:20 分(基本不用)
但问题是,这套规则对车载场景来说太粗糙了。你想想看,车机连着一个信号满格的 2.4GHz Wi-Fi,但实际速度只有 5Mbps;而 5G 蜂窝信号虽然只有两格,但速度能到 100Mbps。系统默认会选 Wi-Fi,因为它的基础分更高。这显然不合理。
所以,我们需要定制评分规则。在 Android Automotive 中,你可以通过 NetworkScoreManager 来干预评分过程。具体做法是注册一个 NetworkScoreCallback:
NetworkScoreManager scoreManager =
context.getSystemService(NetworkScoreManager.class);
scoreManager.registerNetworkScoreCallback(
new NetworkScoreCallback() {
@Override
public int onScoreNetwork(@NonNull Network network,
@NonNull NetworkCapabilities caps) {
int baseScore = 0;
// 1. 基础分:根据传输类型
if (caps.hasTransport(TRANSPORT_ETHERNET)) {
baseScore = 100;
} else if (caps.hasTransport(TRANSPORT_WIFI)) {
baseScore = 60;
} else if (caps.hasTransport(TRANSPORT_CELLULAR)) {
baseScore = 50;
}
// 2. 带宽加分:下行带宽越高越好
int bandwidth = caps.getLinkDownstreamBandwidthKbps();
if (bandwidth > 50000) { // 大于 50Mbps
baseScore += 20;
} else if (bandwidth > 10000) { // 大于 10Mbps
baseScore += 10;
}
// 3. 信号扣分:信号太差要降级
int signalStrength = caps.getSignalStrength();
if (signalStrength <= 1) {
baseScore -= 15;
}
// 4. 计量网络扣分:如果是按流量计费的,扣 10 分
if (!caps.hasCapability(NET_CAPABILITY_NOT_METERED)) {
baseScore -= 10;
}
return Math.max(0, baseScore);
}
});
这里有个设计思路我想分享给你:评分规则要「软硬结合」。硬指标是带宽、延迟、丢包率这些客观数据;软指标是用户偏好、应用场景、流量成本这些主观因素。比如:
- 导航应用:更看重延迟,低延迟的网络加分
- 地图下载:更看重带宽和不限流量,高带宽且非计费网络加分
- OTA 升级:只允许在 Wi-Fi 或以太网上进行,蜂窝网络直接扣到负分
我个人习惯把评分规则做成可配置的,放在 /vendor/etc/network_score_config.xml 里。这样产品经理想调整策略时,不用改代码,改个配置文件就行。嗯,这招帮我省了不少事。
3.4 实战中的避坑指南
最后,我总结几个实战中容易踩的坑:
我曾经... 遇到过一个问题:车机在 Wi-Fi 和蜂窝之间频繁切换,导致网络连接断断续续。后来发现是评分规则太敏感了,Wi-Fi 信号稍微波动一下,分数就低于蜂窝了。
解决方案: 引入「滞回区间」。比如 Wi-Fi 分数要低于蜂窝 15 分以上,才触发切换。这样避免了「乒乓效应」。
另一个坑: 有些车载以太网是通过 USB 转接的,系统可能识别不到它的传输类型。我建议你在 NetworkCapabilities 里检查 TRANSPORT_USB,或者直接看 LinkProperties 里的接口名。
好了,这一章的内容就到这里。NetworkRequest 和 NetworkCapabilities 是车载网络优化的基石,评分规则是灵魂。下一章我会讲如何监控网络状态变化,以及怎么优雅地处理网络切换。到时候见。