3、网络请求与评分机制:NetworkRequest 构建、NetworkCapabilities 详解、评分规则定制

好,咱们进入第三章。这一章我打算聊聊车载网络连接里最核心的两个东西——NetworkRequestNetworkCapabilities。说白了,就是「你想要什么样的网络」和「系统给你什么样的网络」之间的匹配游戏。

我在做第一代智能座舱项目时,就吃过这个亏。当时车机连上了 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);
        }
    });
注意: 这个 API 在 Android 12 之后才稳定可用。早期版本你可能需要修改 ConnectivityService 的源码,或者通过反射来实现。我曾经在 Android 10 的项目上做过类似的事,那叫一个折腾。

这里有个设计思路我想分享给你:评分规则要「软硬结合」。硬指标是带宽、延迟、丢包率这些客观数据;软指标是用户偏好、应用场景、流量成本这些主观因素。比如:

  • 导航应用:更看重延迟,低延迟的网络加分
  • 地图下载:更看重带宽和不限流量,高带宽且非计费网络加分
  • 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 是车载网络优化的基石,评分规则是灵魂。下一章我会讲如何监控网络状态变化,以及怎么优雅地处理网络切换。到时候见。