ConnectivityService 深度解析:服务启动流程、网络评分机制、网络切换策略
各位同学,今天我们来啃一块硬骨头——ConnectivityService。说实话,这个服务是Android Automotive网络连接的核心中的核心。我当年刚接触车载系统时,被它绕得晕头转向。后来花了整整两周时间,把源码从头到尾捋了一遍,才算真正搞明白。
嗯,咱们今天不讲虚的,直接上干货。我会从三个维度来拆解:服务怎么启动的、网络怎么评分的、以及网络怎么切换的。这三个点搞懂了,车载网络连接这块你基本就通了。
一、服务启动流程:从SystemServer到ConnectivityService
先说说启动流程。ConnectivityService是在SystemServer里被创建的。你想想看,系统启动时,SystemServer会依次启动各种服务,ConnectivityService就是其中之一。
具体来说,启动顺序是这样的:
- SystemServer启动,进入run()方法
- 调用startOtherServices(),这里面会创建ConnectivityService
- ConnectivityService构造函数执行,初始化各种内部状态
- 调用systemReady(),完成最后的准备工作
我记得有一次排查一个启动崩溃问题,发现是某个网络模块在ConnectivityService还没初始化完成时就尝试注册监听器,结果直接空指针了。这个坑我踩过,你们要注意。
来看一段核心代码,这是ConnectivityService构造函数的简化版:
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
super(context);
mContext = context;
mNetd = netd;
mStatsService = statsService;
mPolicyManager = policyManager;
// 创建网络评分器
mNetworkRanker = new NetworkRanker();
// 创建网络工厂
mNetworkFactory = new NetworkFactory(context, this);
// 注册各种广播接收器
registerReceivers();
// 加载持久化配置
loadPersistentConfiguration();
}
关键点:ConnectivityService的初始化顺序很重要。如果某个依赖服务还没准备好,就会出问题。我建议在systemReady()回调里做真正的业务初始化,构造函数里只做轻量级工作。
二、网络评分机制:凭什么选这个网络?
好,服务启动完了,接下来要面对一个实际问题:车上有多个网络可用时,系统怎么选?
说白了,就是网络评分机制在起作用。每个网络都有一个分数,分数高的优先使用。但评分标准是什么?我给你们拆解一下。
Android的NetworkCapabilities里定义了很多能力指标:
| 评分项 | 权重 | 说明 |
|---|---|---|
| 网络类型 | 高 | 以太网优先于Wi-Fi,Wi-Fi优先于蜂窝 |
| 信号强度 | 中 | 信号越强,分数越高 |
| 带宽 | 中 | 实测带宽,不是理论值 |
| 延迟 | 中 | 延迟越低越好 |
| 是否计量 | 低 | 非计量网络加分 |
| 验证状态 | 高 | 未验证的网络直接降权 |
我在项目中遇到过一个问题:车载以太网明明连上了,但系统还是优先用Wi-Fi。查了半天,发现是以太网的NetworkCapabilities里没有设置NET_CAPABILITY_INTERNET标志。系统认为这个网络不能上网,所以给了低分。
避坑指南:我曾经在调试车载以太网时,发现网络评分总是偏低。后来发现是NetworkAgent没有正确上报能力集。记住,每个网络必须明确声明它能提供什么能力,否则系统不会给它高分。
评分算法的核心逻辑在NetworkRanker类里。它会对每个网络进行综合打分,然后排序。代码大致是这样的:
private int calculateScore(NetworkAgentInfo nai) {
int score = 0;
// 基础分:网络类型决定
score += getBaseScore(nai.networkInfo.getType());
// 能力加分
if (nai.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
score += 50;
}
// 信号强度加分
score += nai.networkCapabilities.getSignalStrength() * 10;
// 验证状态
if (nai.lastValidated) {
score += 40;
}
return score;
}
嗯,这里要注意,评分不是一成不变的。网络状态变化时,ConnectivityService会重新评分,然后决定是否切换。
三、网络切换策略:什么时候切?怎么切?
评分机制搞清楚了,接下来就是切换策略。说白了,就是系统什么时候决定从一个网络切换到另一个网络。
切换的触发条件主要有这么几种:
- 新网络出现:比如插入了以太网线,或者搜索到了新的Wi-Fi
- 当前网络变差:信号弱了、延迟高了、或者验证失败了
- 当前网络断开:这个最直接,必须切
- 用户手动选择:用户指定用哪个网络
切换策略的核心逻辑在remeasureNetworks()和updateNetworkScore()这两个方法里。我给你们画个流程图:
// 伪代码:网络切换决策流程
void onNetworkStateChanged() {
// 1. 重新评估所有网络
remeasureNetworks();
// 2. 找出分数最高的网络
NetworkAgentInfo bestNetwork = findBestNetwork();
// 3. 如果当前网络不是最优的
if (mCurrentNetwork != bestNetwork) {
// 4. 检查是否可以切换
if (shouldSwitchTo(bestNetwork)) {
// 5. 执行切换
switchNetwork(bestNetwork);
}
}
}
警告:车载环境下,网络切换不能太频繁。我曾经见过一个案例,车辆在隧道口来回进出,导致网络在4G和Wi-Fi之间反复切换,用户体验极差。解决方案是引入切换延迟和切换阈值,避免乒乓效应。
具体来说,我建议在车载系统中做以下优化:
- 增加切换延迟:新网络出现后,等待3-5秒再切换,避免短暂信号波动导致切换
- 设置评分阈值:新网络的分数必须比当前网络高至少20分才切换
- 保留历史记录:记录网络的历史表现,避免切换到不稳定的网络
- 考虑业务场景:视频流媒体场景下,优先考虑带宽;导航场景下,优先考虑延迟
我记得有一次做车载OTA升级测试,发现升级过程中网络突然切换了,导致下载中断。后来我们在ConnectivityService里加了一个"正在下载"的标志位,在关键数据传输期间禁止网络切换。这个方案后来被用在了多个项目中。
总结一下
好了,今天的内容就到这里。ConnectivityService的三个核心点:
- 启动流程:SystemServer创建,注意初始化顺序
- 评分机制:多维度打分,能力集是关键
- 切换策略:避免乒乓效应,考虑业务场景
下一章我会讲NetworkFactory和NetworkAgent的具体实现,到时候会结合车载以太网的实际案例来展开。你们先把今天的内容消化一下,有问题随时问我。
记住一句话:车载网络连接,稳定比速度更重要。别为了追求高分而频繁切换,用户体验才是第一位的。