3、Automotive 项目结构解析:深入理解 Automotive 项目的目录结构、AndroidManifest.xml 的特殊配置、以及 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 项目的核心模块、清单配置和权限体系串起来。
3.5 避坑指南:我踩过的几个坑
最后,分享几个我实际项目中遇到的坑,希望能帮你省点时间。
- 坑一: 忘记加
CAR_LAUNCHERcategory,应用图标不显示。解决方案:检查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 的项目结构其实不复杂,但细节很多。你只要记住:模块划分要清晰,清单配置要准确,权限声明要完整。这三条做到了,项目骨架就稳了。