ConnectivityService 深度解析:服务启动流程、网络评分机制、网络切换策略

各位同学,今天我们来啃一块硬骨头——ConnectivityService。说实话,这个服务是Android Automotive网络连接的核心中的核心。我当年刚接触车载系统时,被它绕得晕头转向。后来花了整整两周时间,把源码从头到尾捋了一遍,才算真正搞明白。

嗯,咱们今天不讲虚的,直接上干货。我会从三个维度来拆解:服务怎么启动的、网络怎么评分的、以及网络怎么切换的。这三个点搞懂了,车载网络连接这块你基本就通了。

一、服务启动流程:从SystemServer到ConnectivityService

先说说启动流程。ConnectivityService是在SystemServer里被创建的。你想想看,系统启动时,SystemServer会依次启动各种服务,ConnectivityService就是其中之一。

具体来说,启动顺序是这样的:

  1. SystemServer启动,进入run()方法
  2. 调用startOtherServices(),这里面会创建ConnectivityService
  3. ConnectivityService构造函数执行,初始化各种内部状态
  4. 调用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之间反复切换,用户体验极差。解决方案是引入切换延迟和切换阈值,避免乒乓效应。

具体来说,我建议在车载系统中做以下优化:

  1. 增加切换延迟:新网络出现后,等待3-5秒再切换,避免短暂信号波动导致切换
  2. 设置评分阈值:新网络的分数必须比当前网络高至少20分才切换
  3. 保留历史记录:记录网络的历史表现,避免切换到不稳定的网络
  4. 考虑业务场景:视频流媒体场景下,优先考虑带宽;导航场景下,优先考虑延迟

我记得有一次做车载OTA升级测试,发现升级过程中网络突然切换了,导致下载中断。后来我们在ConnectivityService里加了一个"正在下载"的标志位,在关键数据传输期间禁止网络切换。这个方案后来被用在了多个项目中。

总结一下

好了,今天的内容就到这里。ConnectivityService的三个核心点:

  • 启动流程:SystemServer创建,注意初始化顺序
  • 评分机制:多维度打分,能力集是关键
  • 切换策略:避免乒乓效应,考虑业务场景

下一章我会讲NetworkFactory和NetworkAgent的具体实现,到时候会结合车载以太网的实际案例来展开。你们先把今天的内容消化一下,有问题随时问我。

记住一句话:车载网络连接,稳定比速度更重要。别为了追求高分而频繁切换,用户体验才是第一位的。