3. Android Automotive 系统架构

好,我们直接进入正题。当你从 Google 拉下一个 Automotive 项目,或者用 Android Studio 新建一个 Car App 工程时,第一眼看到那个目录结构,可能会觉得「嗯,跟普通手机 App 差不多嘛」。

但别急,我刚开始也这么想。结果第一次编译 Automotive 的嵌入式系统镜像时,就踩了坑。说白了,Automotive 项目在普通 Android 项目的基础上,多了好几层「车规级」的东西。今天我们就把它拆开看看。

3.1 项目顶层目录:多出来的那些「车」东西

一个标准的 Automotive 项目,顶层结构大概长这样:

MyCarApp/
├── app/                    # 主应用模块(跟手机一样)
├── car-service/            # 车载服务模块(新增)
├── car-lib/                # 车载库模块(新增)
├── common/                 # 公共工具模块
├── build.gradle.kts        # 根构建文件
├── settings.gradle.kts     # 项目设置
└── AndroidManifest.xml     # 合并后的清单(注意!)

这里我重点说两个目录:car-service/ 和 car-lib/。它们不是随便建的,而是 Automotive 架构里「服务化」思想的体现。

模块 作用 典型内容
app/ 用户界面层,处理交互 Activity、Fragment、ViewModel
car-service/ 后台服务层,常驻运行 CarService、MediaBrowserService
car-lib/ 共享库,给多个模块用 Car API 封装、工具类
common/ 跨模块公共代码 数据模型、常量、网络层

我个人习惯把 car-service/ 单独拆出来,因为车载服务需要声明为系统级服务,跟 UI 模块的权限和生命周期完全不同。你想想看,如果混在一起,一个权限声明错误,整个服务可能起不来。

关键点: Automotive 项目里,car-service/ 模块的 AndroidManifest.xml 必须声明 android:sharedUserId="android.uid.system",否则拿不到系统权限。这个我后面会细说。

3.2 AndroidManifest.xml 的特殊配置:不只是加个标签

普通 App 的清单文件,我们主要配 Activity、权限、Service。但 Automotive 的清单文件,多了几个「车规级」配置项。我直接拿一个真实项目的片段来说:

<!-- app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mycarapp">

    <!-- Automotive 特有权限 -->
    <uses-permission android:name="android.car.permission.CAR_INFO" />
    <uses-permission android:name="android.car.permission.CAR_SPEED" />
    <uses-permission android:name="android.car.permission.CAR_ENERGY" />

    <application
        android:name=".CarApplication"
        android:allowBackup="false"
        android:persistent="true"   <!-- 注意这个属性 -->
        android:sharedUserId="android.uid.system">

        <!-- Car App 入口 Activity -->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <!-- Automotive 必须加这个 category -->
                <category android:name="android.car.intent.category.CAR_LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- 车载服务声明 -->
        <service
            android:name=".MyCarService"
            android:exported="true"
            android:permission="android.car.permission.CAR_SERVICE">
            <intent-filter>
                <action android:name="android.car.intent.action.CAR_SERVICE" />
            </intent-filter>
        </service>

    </application>
</manifest>

这里有几个坑,我一个个说。

3.2.1 android:persistent="true" 的含义

这个属性在普通 App 里几乎不用,但在 Automotive 里很常见。它告诉系统:这个应用要常驻内存,系统启动时自动拉起,用户不能手动停止。

嗯,这里要注意:不是所有 Automotive 应用都需要这个属性。只有那些核心服务类应用(比如车载设置、车辆状态监控)才需要。如果你的应用只是一个音乐播放器,加了这个反而浪费资源。

3.2.2 android.car.intent.category.CAR_LAUNCHER

这个 category 是 Automotive 特有的。没有它,你的应用图标不会出现在车载桌面上。我遇到过有人把普通手机 App 直接往车机上装,结果图标死活不显示,就是少了这个 category。

3.2.3 android:sharedUserId="android.uid.system"

这个配置,说白了就是让你的应用获得系统级权限。但代价是什么?你的应用必须使用系统签名才能安装。如果你只是做第三方应用开发,这个配置可以不加。但如果你做的是系统内置应用,那就必须加。

警告: 加了 sharedUserId="android.uid.system" 后,你的应用不能再通过普通方式安装(比如 adb install)。必须用系统签名打包,或者放到系统镜像里一起编译。我曾经因为这个折腾了一整天,最后发现是签名问题。

3.3 Automotive 特有的权限声明:不只是「读写」那么简单

Automotive 的权限体系比手机复杂得多。手机权限主要围绕「隐私」(位置、相机、联系人),而车机权限围绕「车辆控制」和「车辆信息」。

我整理了一份常用 Automotive 权限表,你直接收藏就行:

权限名称 保护级别 用途
android.car.permission.CAR_INFO signature 读取车辆基本信息(VIN、品牌、型号)
android.car.permission.CAR_SPEED signature 读取当前车速
android.car.permission.CAR_ENERGY signature 读取电池/燃油状态
android.car.permission.CAR_MILEAGE signature 读取总里程
android.car.permission.CAR_CONTROL signature|privileged 控制车辆功能(空调、车窗、门锁)
android.car.permission.CAR_SERVICE signature 绑定车载服务
android.car.permission.CAR_PROJECTION signature 投屏相关(Android Auto)

你看,这些权限的保护级别基本都是 signature。这意味着什么?只有跟系统使用相同签名的应用才能申请。第三方应用基本拿不到这些权限。

那第三方应用怎么办?别急,Automotive 提供了 CarPropertyManager 和 CarSensorManager 等 API,它们会通过系统服务间接提供数据,不需要直接声明这些权限。但前提是,系统服务已经帮你处理好了权限校验。

小技巧: 如果你在开发系统内置应用,建议在 AndroidManifest.xml 里显式声明所有用到的 Automotive 权限。虽然系统签名应用可以「隐式」获得一些权限,但显式声明能让代码更清晰,也方便后续审查。我一般会在清单文件顶部加一个注释块,列出所有 Automotive 权限及其用途。

3.4 知识结构总览:一张图看懂 Automotive 项目

说了这么多,我们来画一张结构图,把 Automotive 项目的核心模块、清单配置和权限体系串起来。

Automotive 项目结构总览 项目模块 • app/ (UI 层) • car-service/ (服务层) • car-lib/ (共享库) • common/ (公共模块) • build.gradle.kts AndroidManifest.xml 特殊配置 • persistent="true" • CAR_LAUNCHER category • sharedUserId 系统签名 • CarService 声明 • 权限声明 权限体系 • CAR_INFO (车辆信息) • CAR_SPEED (车速) • CAR_ENERGY (能源) • CAR_CONTROL (控制) • CAR_SERVICE (服务) 核心逻辑 项目模块决定代码组织方式 → 清单配置决定系统行为 → 权限体系决定能力边界 模块划分 清单配置 权限声明 开发流程建议 1. 先确定应用类型(系统内置 vs 第三方)→ 决定是否需要 sharedUserId 和 persistent 2. 根据功能需求选择模块(UI 放 app/,服务放 car-service/,共享代码放 car-lib/) 3. 在清单文件中声明所有需要的 Automotive 权限,并确保使用系统签名

3.5 避坑指南:我踩过的几个坑

最后,分享几个我实际项目中遇到的坑,希望能帮你省点时间。

  • 坑一: 忘记加 CAR_LAUNCHER category,应用图标不显示。解决方案:检查 intent-filter 里是否包含 android.car.intent.category.CAR_LAUNCHER。
  • 坑二: 服务声明了 android:persistent="true",但没加系统签名,导致服务启动失败。解决方案:确认 sharedUserId 和签名一致。
  • 坑三: 申请了 CAR_CONTROL 权限,但运行时仍然报 SecurityException。原因:该权限是 signature|privileged 级别,普通系统应用可能不够,需要是特权应用(放在 /system/priv-app 目录下)。
  • 坑四: 多个模块的 AndroidManifest.xml 合并时,权限声明冲突。解决方案:在 app/ 模块的清单文件中使用 tools:replace 或 tools:remove 显式控制合并行为。

嗯,差不多就这些。Automotive 的项目结构其实不复杂,但细节很多。你只要记住:模块划分要清晰,清单配置要准确,权限声明要完整。这三条做到了,项目骨架就稳了。