做Android车载显示开发这几年,一个很深的感触:手机App的教程铺天盖地,但一落到车机屏幕上,很多东西就“失灵”了——同样的布局代码,放在1920x720的横屏上跑得歪七扭八;同样的列表滑动,在车机这个“弱鸡”处理器上卡成PPT;想连个OBD盒子读个数据,又发现USB权限这一关就卡了半天。这篇文章我结合自己实际趟过的路,把Android车载显示开发从环境搭建、屏幕适配、性能优化到外设通信、发布排查讲透。无论你是刚接手车机项目的Android开发,还是准备把手机App往车载屏上搬,这里面的坑和建议,应该能帮你省下不少在会议室里抓耳挠腮的时间。
1. 车载显示开发,为什么不能照搬手机App的思路
1.1 先搞清楚你面对的是哪种“车载Android”
车载开发的第一步,其实是搞明白你要做的是哪一层。很多新人看到“车载显示”就以为是一个横屏的Android应用程序,忙活半天发现自己连编译环境都过不了。根据我在行业里看到的实际情况,车载Android开发大体分三种形态:
- AOSP深度定制:国内车厂的绝对主流。厂商拿AOSP源码自己改SystemUI、Launcher、设置等系统应用,然后预装到车机芯片上。这个模式下你改的是“系统级代码”,得会用Android.bp/Android.mk、make、Push到板子上验证,Debug的方式和普通App完全不同。
- Android Automotive OS(AAOS):Google官方的车载系统,国内也有一些海外项目在用。它在AOSP基础上引入了CarService、CarAppLibrary等组件,对第三方应用有严格的规范。比如你要读取车速、续航这类车辆信息,得通过Car APIs,而不是自己去搞串口。
- 车载App应用层:这个最接近常规Android开发,就是做一个APK往车机里装。难点不在代码,而在适配和权限限制——你既拿不到系统权限,又要在车厂自研的ROM上活得好。
我接触的项目里,绝大多数团队是做第二种和第三种,或者三种混着来。所以你接手一个车载项目,第一件事绝对不是打开Android Studio,而是先问:我们要交付的是一个系统镜像,还是一个车机App?这决定了你后续的工具链、调试方式和验收标准。
1.2 车机屏幕和手机屏的差异,比你想的大得多
单纯从显示参数来说,车机屏和手机屏就差得很远。手机屏我们天天见,大多是1080x2400、19.5:9的长条比例;车机屏则常见1920x720(超宽比例8:3)、1920x1080,竖屏反而不多。这个比例对布局的影响是颠覆性的——你手机上用ConstraintLayout拉出来的比例,拿到车机上直接变形。
再看密度。手机普遍是420dpi到480dpi,密度值在2.75到3.0左右;车机为了省成本、保证稳定,很多方案用的是160dpi到320dpi之间,也就是density=1.0到2.0。同样是1920px宽的屏幕,在手机上是到640dp的布局空间,在车机上可能是960dp甚至更多。这就是为什么“同样的代码,效果完全不一样”。
不要小看这个差异,它会让很多你以为理所当然的写法失效。比如你在手机上写死dp宽度来适配几个按钮,放到车机上会变得又宽又挤;你用WindowManager拿到屏幕宽高做百分比,车机上因为顶栏、状态栏、安全边距的存在,算出来的可用区域往往是错的。
我觉得最稳妥的做法,是拿到车机真机实测第一手数据:分辨率、dpi、物理尺寸、可用显示区域,这些先写进团队的技术文档里,再开始写布局。别看这些数据在网上能查到,厂商定制之后经常会有出入。
1.3 处理器、内存和系统裁剪,决定了你的代码上限
车载显示的性能预算,远比手机紧张。现在主流车机芯片虽然已经是8核起步,但考虑到车载环境无风扇散热、电源波动、长时间持续工作,厂商在调校上都会偏向保守,实际的CPU调频策略不会像手机那么激进。内存方面,很多车机还是4GB到8GB,你要在上面同时跑桌面、导航、音乐、车控服务,留给单个应用的内存非常有限。再加上厂商为了稳定和安全,会裁剪掉大量系统组件——比如去掉GMS、砍掉某些IPC服务、限制后台应用数量——你在手机上调得好好的代码,在车机上可能连一个Service都起不来。
这里我特别想强调一点:车载显示不是“屏幕大了点的Android”,而是一个要连续工作十几个小时、被强光直射、还可能被用户的手指不精确地点按的显示终端。它的设计目标不是酷炫,而是稳定、可读、可预测。理解了这一点,后面所有的技术选型和适配思路就都顺了。
2. 环境搭建与调试链路:模拟器只是个“幻觉”
2.1 Studio版本、SDK与AGP的匹配问题
车载项目最先劝退新人的,往往是工程本身编译不过。我遇到过太多次的报错,根因不过是Android Studio版本、AGP版本、Gradle版本三者不匹配。以热搜里常见的android studio 2023.1.1.16 windows.exe为例,这个版本对应的AGP范围是8.1到8.2左右,Gradle需要8.2以上。如果你拿到一个车厂给的旧工程,用的是AGP 7.x、Gradle 7.4,非要拿最新版Studio直接打开,大概率会出现各种DSL配置报错。
我个人的习惯是先不看Studio的新功能,先确认工程里的gradle-wrapper.properties和build.gradle里AGP的版本号,再去装匹配的Studio。如果你下车厂工程,他们通常会给一个“已验证的环境清单”,照着装最稳。Android Studio能不能设置成中文?能,在Settings里搜Language或者直接装中文语言包,但我真的不建议——因为你在搜代码问题、看报错、查官方文档时,英文关键词才是通用的,中文菜单反而增加了一次“翻译”的认知负担。
2.2 为什么普通Android模拟器不适合车载开发
很多新人接手车载显示项目后,第一反应是装个模拟器先跑起来。我懂这种心情,但普通手机模拟器在车载场景下的价值真的有限。原因很简单:手机上跑的是带GMS的系统镜像,没有CarService,也没有厂商定制的那一堆系统应用,你的应用依赖的很多系统接口根本不存在。
如果一定要用模拟器,我建议直接用Google官方提供的Automotive镜像——Android Studio的Device Manager里支持创建Automotive模拟器,它能模拟仪表盘、中控等多屏环境,还有CarService。但它的问题是性能拉胯、启动慢、和你最终交付的厂商ROM差异还是很大。所以我的结论是:模拟器只适合验证基本的UI排版逻辑,绝不适合做车载开发的主战场。哪怕是一个二手的车机开发板,都比模拟器有价值得多。
2.3 用ADB快速模拟车机屏幕参数
在你还没有真机的时候,有一个土办法可以提前验证大部分布局适配问题——用ADB命令强行修改手机的屏幕分辨率和密度,让手机模拟出车机的显示环境。这招我在没有开发板的时候经常用:
adb shell wm size 1920x720 adb shell wm density 160 adb shell wm overscan 0,0,0,0执行完这几个命令,你的手机会立刻变成一台“伪车机”:宽屏、低密度、很“矮”的可用空间。此时跑你的App,大部分布局问题会立刻现形:按钮太大、列表错位、底部导航被挤压等。调试完恢复也很简单:adb shell wm size reset和adb shell wm density reset。
要提醒一句:这种方法只改了显示参数,改不了系统裁剪和CarService,所以它适合做“布局适配自测”,不适合做“功能验证”,千万别拿这个结果向车厂交差。
2.4 签名机制:从debug到系统签名的跳跃
普通Android开发对签名的理解多半停留在“打包用release签名”。车载项目里,签名问题要复杂得多。车机App至少会碰到三种签名:
- Debug签名:开发自用,
debug.keystore,没什么好说的。 - Release签名:应用厂商自己的正式签名,用于应用市场分发。
- 系统签名:用厂商的
platform.pk8和platform.x509.pem对APK签名。这个签名一旦用上,你的应用就拥有系统级权限,能干很多普通App干不了的事——比如直接读写系统文件、申请一些signature|privileged级别的权限。
车厂给的APK,十有八九会要求你最后用系统签名。签名不对,轻则权限拒绝,重则安装直接失败。而且系统签名文件属于厂商的高度机密,一般只会在一个固定的构建机上操作,不会给到每个开发人员。所以你在本地调得好好的,一提交到厂商的构建服务器就“消失”了,不用太惊讶,查一下签名环节基本能定位。
3. 车载屏幕适配实战:从尺寸计算到复杂布局落地
3.1 用一个小公式解决90%的尺寸适配问题
车载屏幕的适配,归根结底是搞清楚dp和px的关系。公式很简单:dp = px / (dpi / 160)。而dpi和density的关系是:density = dpi / 160。也就是说,dp = px / density。
举个例子。一块1920x720分辨率、density=2.0的车机屏,它的dp空间就是960x360。这个“矮胖”的坐标系,就是你的整个世界。做布局的时候,我会在代码里加一段调试工具,把真实的参数打出来:
val dm = resources.displayMetrics Log.d("ScreenInfo", "px=${dm.widthPixels}x${dm.heightPixels}, dpi=${dm.densityDpi}, density=${dm.density}") Log.d("ScreenInfo", "dp=${dm.widthPixels / dm.density}x${dm.heightPixels / dm.density}")在车机上,我强烈建议所有的布局尺寸都基于dp,而不是px。因为厂商可能在同一个硬件上输出不同的分辨率(比如切换兼容模式),如果你用了px,一改分辨率界面就稀碎;用dp加合理的权重分配,至少能保持结构稳定。
3.2 安全区域:圆角、黑边和“非显示区”
车载屏幕的物理形态五花八门,有带大圆角的,有被中控面板遮挡一部分的,还有故意保留黑边给空调出风口让路的。如果你把内容顶格绘制,后果可能是按钮被屏幕边缘切掉一半,或者地图的关键操作被圆角遮住了。这个问题在手机上也有,但车机上更夸张——因为屏幕不是矩形,或者说是矩形但显示区域被物理遮挡。
Android从API 29开始有WindowInsets,能拿到系统栏和显示裁切区域。但在大多数厂商定制ROM上,这一步并不可靠,因为厂商的“安全区”概念不一定映射到标准insets上。更实际的做法是拿到厂商提供的显示参数,或者干脆用adb shell wm overscan来模拟。我在项目里会保留一个全局的SafeInsetsManager,统一读取所有屏幕的可用区域,所有Activity都基于它设置padding。这样做的好处是:当厂商告诉我“右上角要留80px给Logo”,我只需要改一个地方,而不是满工程找padding。
3.3 协调布局+Banner在车机上的正确打开方式
热搜里有个词是“android中协调布局+banner”,这确实是车载显示中很常见的组合。典型场景是:顶上一个Banner轮播(促销信息、天气预警、充电状态),下面一个可滚动的消息列表或WebView,Banner在滚动过程中逐渐收缩并让位给工具栏。
用CoordinatorLayout实现,核心是自定义一个Behavior,监听下方滚动View的滚动距离,动态调整Banner的TranslationY和高度。这个思路在手机上很流行,但车载场景要注意两个点:
第一,动画时长和距离要克制。驾驶场景下,用户只能接受视线离开路面很短的时间,如果你的Banner在滚动时来一个大范围位移动画,很容易让人感觉画面在“闪”。我一般会把动画限制在300毫秒以内,位移范围也不宜过大。
第二,注意Banner复用。车载系统的桌面和仪表会同时跑多个页面,内存本来就紧张,如果每个Banner都自己管理一个ViewPager2和几个ImageView,资源开销会很快拖垮整机。更稳的方案是Banner只保留当前页和左右相邻页,图片用统一的生命周期管理,离开页面时主动释放。这些在手机上占比不高的资源,在车机上就是生死线。
3.4 夜间模式、九宫格和按钮尺寸的细节
车载屏白天被阳光直射,晚上又要在暗光环境里看,所以夜间模式是刚需。Android原生有values-night资源目录和UiModeManager,车机上一般会通过光线传感器或手动开关切换。我常用的方案是定义两套颜色变量,风格上白天用高对比、夜晚用低饱和的深灰色(不要纯黑,纯黑在暗光下容易有拖影感,而且在部分LCD屏上反而更刺眼)。
还有两个小细节,我在代码评审里经常提:
- 点击目标最小44dp:车载触控屏的误触率比手机高得多,因为车辆在颠簸,手指的落点抖动很厉害。44dp是底线,核心操作建议做到至少56dp。
- 车机桌面的九宫格:这个被搜索了很多次的功能,其实就是一个GridView/RecyclerView的GridLayoutManager,关键是图标资源要支持动态更新——比如根据车辆状态切换图标主题,你的icon要按主题拆分,而不是把所有状态画死在一张图上。做动态图标主题的时候,别忘了用
StateListDrawable或用setCompoundDrawableTintList按状态着色,否则后续换皮肤会非常痛苦。
4. 性能与稳定性:在“比手机弱的硬件”上做又大又稳的界面
4.1 启动速度优化:开机到首屏,每一帧都要省
车载显示的启动分两种:一种是应用冷启动,一种是开机到桌面的系统级启动。后者在厂商侧有一套考核指标,常见的是从上电到Launcher首帧显示控制在5秒内。这里面的关键,是减少主线程上的无用功。
我做过的启动优化,核心思路无非这几点:
- 任务分级。把Application的初始化拆成“必须同步”“必须异步”“可延迟”三类。比如崩溃上报、基础日志必须第一时间建好;网络库、图片库放到IO线程初始化;像语音引擎这种重资源,可以等用户第一次进入相关页面再加载。
- 首帧只画关键内容。不要在首帧之前等网络数据,先画出来一个骨架屏或者本地缓存数据,然后再异步刷新。
- WebView预加载要谨慎。车载很多页面是WebView壳的,如果你在Application里预创建WebView,启动时间会直线上升。一般做法是先不创建,等第一次要用了再预热的冷启动。
启动速度优化是典型的“一票否决”项,你不会因为做好了加分,但做不好一定会被怼。
4.2 用Perfetto找出掉帧的真凶
车载显示的卡顿,很多不是某一个方法特别慢,而是多个任务争抢主线程。比如滚动列表时,另一处正在解析JSON;Banner动画时,主线程在等IO。这些情况用肉眼看很难定位,打开Perfetto才是正路。
先抓一段trace(车机支持的话):
adb shell perfetto -o /data/misc/perfetto-trace/trace.perfetto-trace -t 10s sched freq idle binder_driver gfx view adb pull /data/misc/perfetto-trace/trace.perfetto-trace然后把这个trace拖进ui.perfetto.dev,重点看两个东西:一个是主线程的Choreographer#doFrame间隔,如果间隔超过16.6ms且密集出现,说明有连续掉帧;另一个是各个Binder调用的耗时,很多时候你会发现卡顿源自一个不合理的系统服务IPC,而非你自己的逻辑代码。
我在实际项目里遇到过这样一个案例:一个地图缩放动画,看起来卡得一帧一帧跳。Perfetto一拉,发现是某个系统服务在动画期间频繁回调了一个重量级的跨进程方法,把主线程的调度打乱了。解决办法不是优化动画,而是把这个回调改成了监听式的低频通知,卡顿直接消失。这种事,用“猜”是永远猜不出来的,工具才是亲爹。
4.3 内存和功耗:常亮屏场景下的取舍
车机屏幕一旦点亮,往往是连续亮好几个小时,所以功耗优化和内存优化是绑定在一起的。你少做一次无意义的View刷新,就是在帮整车节省电量,也帮自己减少散热问题。
几个我在车机上特别注意的细节:
- Bitmap是内存大头。用Glide的时候,除了设置
override()和采样,我还会根据屏幕密度做降采样。车机屏PPI不高,很多高清图其实不需要全尺寸加载。加载策略统一走一个GlideModule,不要在业务代码里到处改。 - 减少不必要的invalidate。车载界面上经常有进度条、音量条这类高频刷新控件,最忌讳的是在数据源回调里直接无条件
requestLayout()。正确的做法是做个节流,只在数值真正变化超过阈值时才刷新。 - 及时释放IPC连接。车机上有大量跨进程服务,比如CarService、MediaSession,开发时很容易忘记释放
Connection。泄一个Binder连接不算大事,但泄一整个Service连接在长时间运行后就是另一个“无响应”。
4.4 一个实际优化案例
去年我们做一块仪表娱乐屏,监听到系统启动后桌面首帧时间在7.3秒左右,比指标多了2秒多。拉了一遍trace,发现最大的瓶颈是两张静态壁纸的加载占了600多毫秒——壁纸是4K分辨率,还在主线程里做了BitmapFactory.decodeStream。修复方案很简单:把壁纸解码放到后台线程,先显示纯色背景,解码完成再覆盖;其次发现某厂家的SDK初始化里居然有网络重试,超时3秒才跳过,改成检测到无网络直接跳过。两处改完,首帧时间降到4.6秒。这种问题看起来不高级,但它实实在在影响用户的每一秒体验。
5. 与外设通信:USB串口、蓝牙和车载数据
5.1 USB串口通信的完整流程
热搜里“android usb串口通信”排在很前面,说明这确实是车载显示开发的高频需求。最常见的场景是通过USB口连接OBD诊断仪、传感器盒子或者副屏显示控制器。Android虽然是移动OS出身,但照样支持USB Host模式,可以用UsbManager枚举设备并通信。
完整流程分这几步:
- 在Manifest里声明设备过滤,让系统识别到指定VendorID/ProductID时自动拉起你的App。
- 动态申请USB权限,核心代码如下:
val usbManager = getSystemService(Context.USB_SERVICE) as UsbManager // device 来自 usbManager.deviceList 匹配 val permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0) usbManager.requestPermission(device, permissionIntent)- 拿到权限后建立串口连接。我一般直接用
usb-serial-for-android这个开源库,省去自己处理UsbSerialPort协议的麻烦。它会根据驱动自动找到端口,设置好波特率、数据位、停止位就能读写。
val serialPort = usbSerialPort serialPort.open() serialPort.setParameters(115200, 8, 1, USBSerialPort.PARITY_NONE)车机上有几个额外的坑:部分车机USB口不止一个,你的设备可能插在任意口上,要做设备热插拔监听;还有的车机在休眠时会关闭USB口电源,导致串口设备“消失”,这时候不能傻傻继续轮询,得监听USB断开事件再做重连。这些在普通手机上不太明显,但在长期运行的车机上不做,开发到后期会非常痛苦。
5.2 蓝牙SPP与BLE怎么选
车载场景里蓝牙也很常见:连手机打电话、连手环、连OBD盒子。选型的逻辑很简单:
- SPP(传统蓝牙):适合和自家硬件之间做稳定的数据流传输,吞吐量大、实现简单,车机上很常用。车机系统的蓝牙协议栈通常对SPP支持得还可以。
- BLE(低功耗蓝牙):适合低功耗、小数据量的场景,比如读取传感器状态、手环信息。
我的建议是,如果是自家团队做的硬件,优先用SPP;如果是接入第三方外设,先确认对方用的是哪种协议。BLE的坑在于扫描、连接、服务发现、特征值读写这几步的时序非常敏感,车机蓝牙协议栈偶尔会有不稳定的情况,所以要做超时重试。SPP看着简单,但配对成功不等于连接成功,建议在应用层做心跳保活,比如每5秒发一个短包,3次没有回应就主动断开重连。
5.3 车载数据(车速、电量)不是你想读就能读
很多做车载App的会问:能不能直接读车速、电量和油量?答案很统一:不能,尤其前装车机不能。车辆数据是安全相关的核心资产,车厂不可能让一个普通App直接通过OBD或CAN总线去读。哪怕你是系统应用,很多时候也只被允许读取脱敏后的非实时数据。
目前主流的做法是车厂在中间层提供一套数据服务,App通过Binder/广播去订阅。举个例子:
val carService = getSystemService("car") as Car val propertyManager = carService.getCarPropertyManager() propertyManager.getProperty(CarPropertyManager.PID_MILEAGE, 0)这套接口是否能拿到,取决于厂商开放了哪些权限。如果你做的是后装硬件,可能走的还是串口或者蓝牙读OBD盒子,那又是另一套私有协议。在这个问题上,我劝大家不要想着绕过厂商的限制去拿数据,一旦出事,轻则应用下架,重则惹上安全责任。
5.4 文件传输与数据通道的常见坑
车载显示上经常会有“把日志导出来”“把地图包拷进去”这类需求,于是就会碰到热搜里那一堆content://开头的问题,比如content://com.baidu.searchbox.fileprovider/...。这不是某一家App的问题,而是Android对/storage/emulated/0/Android/data/目录的限制越来越严格,从Android 11开始,应用基本无法直接读取其他应用同一目录下的文件,车机上因为厂商定制,行为更加飘忽。
你要做的,是在自己的应用里实现合法且可控的文件分享,用FileProvider暴露你有权限访问的文件:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider>然后通过FileProvider.getUriForFile()拿到Uri,再配上Intent.FLAG_GRANT_READ_URI_PERMISSION发给目标App。别图省事直接拼file://路径,这在现代Android上几乎必炸。
6. 发布与排查:这些年被问得最多的坑
6.1 APK编译、签名和安装失败
“Android Studio怎么编译成APK”这个问题,在车载场景下答案是:Build > Build Bundle(s) / APK(s) > Build APK(s),就这么简单。但真正的坑在后面——安装失败。车机上最常见的失败原因是下面这几个:
INSTALL_FAILED_UPDATE_INCOMPATIBLE:旧版本签名和新版本不一致。车机的系统应用升级最常遇到,往往需要先卸载旧的,或者用相同的系统签名重打。INSTALL_FAILED_DUPLICATE_PERMISSION:权限被另一个应用声明过,而且权限级别不同。这种情况在六个厂商ROM上都有出现,原因多半是你用了sharedUserId或者声明了和系统应用相同的<permission>。INSTALL_FAILED_NO_MATCHING_ABIS:你的APK只带了arm64-v8a,但车机芯片是32位栈。现在不少车机虽然芯片是64位的,但系统跑的是32位用户态,这类兼容问题会消耗你一个下午。
遇到安装失败,先不要问“为什么”,先跑一遍adb install -r xxx.apk,看完整错误码,再决定排查方向。靠肉眼和猜测解决不了编译问题。
6.2 日志抓取的正确姿势
车载开发最大的痛点是你不能像手机一样随时连电脑。车机在测试车间里跑,你在办公室里,出了问题往往只能靠日志。我的经验是这几条:
- 用logcat分级抓。
adb logcat -v threadtime -b all > log.txt可以抓全核心缓冲区的日志。但车机上日志量巨大,最好先按进程过滤。 - 不要漏了kernel log和tombstone。很多诡异问题(比如触摸失灵、显示撕裂)其实出在内核驱动层,
adb shell dmesg | grep -i error、/data/tombstone/里的native crash文件,往往比logcat更接近真相。 - 在关键业务上加自埋点。车机App要做一个内存可读的环形日志,把关键操作、异常堆栈写进文件。出问题时直接让测试人员拉文件出来,比远程复现高效百倍。
6.3 多屏与副驾屏开发
越来越多的车开始有副驾娱乐屏和后座屏,“多屏开发”就成了一个正经话题。Android上的主流方案是Presentation——在DisplayManager里枚举其他显示器,然后往那块屏上显示一个额外的View层级。代码上核心是按Display创建Presentation并show:
val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager val displays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION) val presentation = MyPresentation(context, displays[0]) presentation.show()多屏的坑主要在焦点管理。副驾屏上的输入事件不会自动跟随,你得自己监听DisplayManager.FLAG_...来判断每块屏是否有焦点。还有一个是资源区分——主屏副屏分辨率可能不同,多屏环境下resourceConfiguration不会自动区分,通常需要靠厂商的定制接口或者自己在代码里手动切换。这块水很深,建议在立项的时候就明确“几块屏、什么尺寸、是否允许同时交互”,别拖到开发后期才想起。
6.4 权限申请汇总与自检清单
车载应用权限的申请和手机差别很大,因为很多权限你是拿不到的。我整理了一张常用表,开发时对照自查:
| 权限 | 手机上 | 车机上 |
|---|---|---|
ACCESS_FINE_LOCATION | 运行时权限 | 运行时权限,但部分车机常驻 |
BLUETOOTH_CONNECT | 运行时权限 | 需要系统签名或厂商白名单 |
USB设备权限 | UsbManager.requestPermission | 系统签名时常可自动授权 |
READ_WRITE_EXTERNAL_STORAGE | Android 11后基本禁用 | 车机上要看厂商具体ROM |
| 车辆属性(车速/续航) | 无此概念 | 必须通过CarService |
自查清单我通常归纳成三条:第一,凡是要系统签名的权限,先确认你是否在厂商的权限白名单里;第二,不申请用不到的权限,这在车厂审核阶段会被重点问;第三,对权限被拒绝的场景做降级设计,不要一拒绝就崩。车机上权限拒绝的后果比手机上严重得多,因为用户可能在开车,不能弹窗让他慢慢看。
其实写了这么多,我想最核心的还是那句话:把车机当车机。它不追求把每个页面做到最炫,但要保证在恶劣环境里用最少的资源,把信息尽可能高效和清晰地传达给正在开车的人。我个人的一点体会是,做车载显示的调试和验证,一定要尽早拿到真实屏幕和真实硬件,哪怕是最低配的开发板,也比在模拟器上反复“感觉良好”强得多。最后分享一个小技巧:适配阶段随身带一张写满设备参数的备忘卡(分辨率、dpi、安全边距、API等级、签名类型),你会在各种会议和现场的沟通中被它拯救无数次。