车载网络与连接:CarNetworkService、CarBluetoothManager、CarProjectionManager、Wi-Fi 热点
各位同学,今天我们来聊聊车载系统里最“接地气”的部分——网络与连接。
说实话,这部分内容在开发中往往被低估。很多人觉得不就是连个网、连个蓝牙嘛,有什么难的?但我在项目中踩过的坑告诉我,恰恰是这些“基础功能”,最容易让用户骂娘。你想想看,用户开车时连不上蓝牙,导航没法用,那体验得多糟糕。
所以今天,我带你把这几个核心服务彻底搞明白。
9.1 CarNetworkService:车载网络的“交通警察”
CarNetworkService 是车载网络连接的总调度。它管理着蜂窝网络、Wi-Fi、以太网等物理链路,并负责向应用层提供统一的网络状态。
我个人习惯把 CarNetworkService 看作一个“交通警察”。它不负责具体的网络收发数据,但它知道哪条路通了、哪条路堵了、哪条路是收费的。
9.1.1 核心职责
- 网络状态监听:实时监测蜂窝、Wi-Fi、以太网的连接状态
- 网络评分与切换:根据信号强度、带宽、延迟等指标,自动选择最优网络
- 网络策略下发:向系统服务和应用广播网络变化事件
关键点:CarNetworkService 不直接处理 TCP/IP 数据包,它只做“网络管理”层面的工作。真正的数据传输,还是走 Android 原生的 ConnectivityManager。
9.1.2 代码示例:监听网络状态变化
// 注册 CarNetworkService 回调
CarNetworkService networkService = car.getCarManager(Car.CAR_NETWORK_SERVICE);
networkService.registerNetworkPolicyListener(new CarNetworkService.NetworkPolicyListener() {
@Override
public void onNetworkPolicyChanged(int networkType, int policy) {
// networkType: CarNetworkService.NETWORK_TYPE_CELLULAR / WIFI / ETHERNET
// policy: CarNetworkService.POLICY_ALLOW / DENY / METERED
Log.d("CarNetwork", "网络类型: " + networkType + " 策略: " + policy);
}
});
嗯,这里要注意:回调是在 binder 线程里触发的,别在里面做耗时操作。我曾经见过一个同事直接在回调里写数据库,结果 ANR 了。
9.1.3 网络评分机制
CarNetworkService 内部有一套评分算法。它会根据以下维度给每个可用网络打分:
| 维度 | 权重 | 说明 |
|---|---|---|
| 信号强度 | 30% | RSSI 或 RSRP 值 |
| 带宽 | 25% | 实测吞吐量 |
| 延迟 | 20% | ping 延迟 |
| 计费属性 | 15% | 是否按流量计费 |
| 稳定性 | 10% | 历史断连频率 |
评分最高的网络会被设为“默认网络”。应用层通过 ConnectivityManager 获取的,就是这个默认网络。
我的经验:在车载场景下,Wi-Fi 的评分往往会被调低。因为车辆在行驶中,Wi-Fi 信号极不稳定。我曾经遇到过一辆车在高速上自动切到路边餐馆的 Wi-Fi,结果导航卡了十分钟。后来我们在评分算法里加了一条“移动速度超过 30km/h 时,Wi-Fi 评分直接减半”。
9.2 CarBluetoothManager:蓝牙连接的“管家”
CarBluetoothManager 是车载蓝牙连接的核心服务。它管理着手机配对、免提通话、蓝牙音乐、电话本同步等功能。
说白了,它就是 Android 原生 BluetoothAdapter 的“车载增强版”。
9.2.1 核心功能
- 设备配对与连接:支持多设备同时连接(比如一部手机打电话,另一部放音乐)
- Profile 管理:HFP(免提)、A2DP(音乐)、AVRCP(控制)、PBAP(电话本)
- 连接优先级:根据用户设置或历史记录,决定先连哪台设备
9.2.2 代码示例:获取已配对设备列表
CarBluetoothManager btManager = car.getCarManager(Car.CAR_BLUETOOTH_SERVICE);
List<CarBluetoothDevice> pairedDevices = btManager.getPairedDevices();
for (CarBluetoothDevice device : pairedDevices) {
Log.d("CarBT", "设备: " + device.getName() + " 地址: " + device.getAddress());
// 检查当前连接的 Profile
boolean isHfpConnected = btManager.isProfileConnected(device, CarBluetoothDevice.PROFILE_HFP);
boolean isA2dpConnected = btManager.isProfileConnected(device, CarBluetoothDevice.PROFILE_A2DP);
}
避坑指南:我曾经在项目里遇到一个诡异的问题——手机连上车机后,音乐播放卡顿。查了两天才发现,是因为 A2DP 和 HFP 同时连接时,蓝牙模块的带宽被 HFP 占用了。解决方案是:在播放音乐时,暂时降低 HFP 的采样率。
9.2.3 多设备连接策略
车载场景下,多设备连接是常态。CarBluetoothManager 支持以下策略:
- 主设备优先:用户指定的主设备拥有最高优先级
- 按 Profile 分配:一部设备连 HFP,另一部连 A2DP
- 自动切换:主设备断开后,自动连接次优设备
我个人建议,在 UI 上给用户一个“设备管理”界面,让用户能手动调整优先级。别全自动,用户会没安全感的。
9.3 CarProjectionManager:手机投屏的“桥梁”
CarProjectionManager 是 Android Automotive OS 里专门管理手机投屏的服务。它支持 Android Auto、Apple CarPlay 等协议。
说白了,它就是车机与手机之间的“翻译官”。手机发来的指令,它翻译成车机能理解的;车机的屏幕,它映射给手机去渲染。
9.3.1 核心流程
- 发现与配对:通过 USB 或 Wi-Fi 发现手机
- 连接建立:建立数据传输通道(通常走 TCP)
- 视频流传输:手机将 UI 渲染成视频流,推送到车机
- 触控事件回传:车机上的触摸事件,回传给手机处理
9.3.2 代码示例:启动投屏会话
CarProjectionManager projectionManager = car.getCarManager(Car.CAR_PROJECTION_SERVICE);
CarProjectionManager.ProjectionSession session = projectionManager.startProjection(
new CarProjectionManager.ProjectionCallback() {
@Override
public void onProjectionStarted(int status) {
if (status == CarProjectionManager.STATUS_SUCCESS) {
Log.d("Projection", "投屏启动成功");
}
}
@Override
public void onProjectionStopped(int reason) {
Log.d("Projection", "投屏停止,原因: " + reason);
}
}
);
注意:投屏会话的生命周期必须与 Activity 绑定。如果 Activity 被销毁,会话必须主动关闭。否则会出现“手机还连着,但车机界面已经没了”的尴尬情况。
9.3.3 性能优化建议
投屏对性能要求很高。我见过一些车机,投屏时卡成幻灯片。这里给几个优化方向:
- 视频解码硬件加速:务必使用 MediaCodec 的硬件解码器
- 帧率控制:根据车机屏幕刷新率,动态调整投屏帧率(一般 30fps 就够了)
- 分辨率自适应:不要硬上 1080p,根据车机屏幕实际分辨率来
9.4 Wi-Fi 热点:车载“移动基站”
Wi-Fi 热点功能,说白了就是把车变成一个移动路由器。乘客的手机、平板都可以通过车载 Wi-Fi 上网。
这个功能在长途旅行中特别实用。我记得有一次坐朋友的车,他车上的 Wi-Fi 热点信号特别好,我们三个人同时看视频都不卡。
9.4.1 核心实现
Android Automotive OS 使用 SoftApManager 来管理 Wi-Fi 热点。CarService 对它做了一层封装,提供了更符合车载场景的 API。
CarWifiManager wifiManager = car.getCarManager(Car.CAR_WIFI_SERVICE);
// 开启热点
wifiManager.startSoftAp("MyCarWiFi", "password123", new CarWifiManager.SoftApCallback() {
@Override
public void onStateChanged(int state) {
// STATE_ENABLED / STATE_DISABLED / STATE_FAILED
}
@Override
public void onClientConnected(String macAddress) {
Log.d("WiFiAP", "新设备接入: " + macAddress);
}
});
9.4.2 热点配置参数
| 参数 | 说明 | 建议值 |
|---|---|---|
| SSID | 热点名称 | 车辆品牌 + 随机后缀 |
| 加密方式 | WPA2-PSK 或 WPA3 | WPA2(兼容性最好) |
| 最大连接数 | 同时连接的设备数量 | 5-8 台(太多会卡) |
| 频段 | 2.4GHz 或 5GHz | 5GHz(干扰少,但穿墙差) |
我的建议:热点名称最好加上随机后缀,防止多辆同品牌车停在一起时,用户连错。另外,密码不要用固定值,每次启动时随机生成,用户可以在车机屏幕上查看。
9.4.3 热点与蜂窝网络的带宽分配
热点用的是蜂窝网络的数据。如果车上同时有多个设备在下载大文件,导航可能会受影响。
我建议做带宽限制:
- 导航流量优先:保证导航应用的最低带宽(比如 1Mbps)
- 热点限速:每个连接设备限速 5Mbps,防止单个设备占满带宽
- 动态调整:当蜂窝信号变差时,自动降低热点速度
知识体系总览
下面这张图,我把今天讲的内容串起来了。你可以看到各个服务之间的关系:
好了,这一章的内容就到这里。车载网络与连接这块,说白了就是“让车能上网、让手机能连车、让乘客能上网”。每个服务都有自己的职责,但最终都要协同工作。
你在实际开发中,如果遇到网络切换不顺畅、蓝牙连接不稳定、投屏卡顿这些问题,可以回头看看这一章的内容。大部分问题,都能从这几个服务的配置和策略里找到答案。