共计 7189 个字符,预计需要花费 18 分钟才能阅读完成。
本文作者:陆康、陈驰枻、聂帅
以后造车新权势越来越火,汽车智能化成为风口,很多手机利用心愿拓展车机场景,云音乐及旗下 Look 直播也在车机端场景进行了一些摸索,上面分享过程中的一些总结和心得体会
目前车载开发的类型和特点
以后车载接入形式次要有三种,第一种是以华为 Hicar 为代表的手机 app 扩大接入,第二种是提供对外的 OpenApi,车企自行研发利用进行接入,最初一种是最为广泛的车机独立 app 接入。
1. 手机 app 扩大接入,以华为 HiCar 为例
这种形式并不要求给车机提供独立的车载版 apk,而是由手机端的利用接入 Hicar sdk,间接在原有的工程上开发。
目前多家手机厂商采纳的车联计划都是基于 Android 零碎自带的 MediaSession 框架进行模板化开发,手机端的利用只须要依据厂商提供的模板筹备数据,具体的 UI 展现由车机设备实现,开发者无需关怀屏幕适配及 UI 格调对立的问题,具体的播控指令同步也是通过 MediaSession 框架实现的。
该接入形式须要本人制订 Media Data Tree 的构造。因为 ViewTree 的展现是交给内部进行渲染的,咱们往往只能通过 onPlayFromMediaId 回调里的 mediaId 和 extras 来获取车机上点击播放的媒体信息,mediaId 能够结构成例如 tab -> page -> listId -> songId 的层级关系,咱们就能够晓得播放的是具体来自哪个页面中哪个歌单中的哪首歌了,这也是 Android 官网的 Universal Android Music Player Sample 中采纳的实现形式。
这种车载接入形式有如下特点
- 接入不便,间接在原有工程根底上开发,根底能力是现成的,交付模式为手机 apk,与原来保持一致
- 适配不便,例如 Hicar 针对不同类型的利用,间接提供了模板化开发的能力,音频利用只需专一于音频数据的筹备和播放服务的实现即可,其它繁琐的工作,例如绘制车机界面并保障各分辨率兼容性、治理音频桌面卡片和实现音频工作接续等都由 HiCar 实现
- 更新不便,只须要手机上的利用更新了即可更新车机展现逻辑,相比更新车机利用,疏导用户的成本低不少
- 适用范围的局限性,即只与特定平台绑定,比方 Hicar 只反对华为手机,并且要求车机接入了华为 Hicar 零碎,目前来说,国产的几家支流手机厂商都在尝试推相似的生态,汽车厂商在互联网造车的势头中,也放慢了这些零碎的引入,但从总量来说,依然属于车机中的少部分
2. OpenAPI 接入
这种实现形式是服务端依据咱们的服务内容提供对应的 OpenAPI 接口。厂商能够自行设计需要计划和视觉计划,依据不同的需要范畴去调用不同的接口来获取数据并展现,然而最初个别须要通过咱们的审核能力公布。这种接入形式中的开发资源也是由厂商本人提供的,承载的平台包含 Linux、Android 等多个零碎环境。因为这种形式不是本文的重点,就不在此赘诉了。
这种接入形式有以下特点
- 我方投入的人力老本小,次要开发成本集中在厂商那一边
- 能够适配各种环境,并不局限于某一种车机系统
- 可控性较小,数据的获取有一部分依赖于厂商提供,我方只能拿到接口调用次数,在波及到结算的问题上容易产生分歧
- 迭代艰难,依赖于厂商本身的开发资源
3. 独立 app 接入
能够看出上述 OpenAPI 的实现形式还是存在一些比拟要害的问题,所以一般来说咱们会优先采纳独立 app 接入的形式,这是目前更为广泛的形式,也是本文次要形容的接入形式。这种形式与手机利用开发其实相似,但也有一些特点
- 车机系统的碎片化相比现在比拟成熟的手机生态(绝大多数份额在头部厂商)更加重大,很多厂商基于 Android 研发本人的车机系统,针对方控、桌面 widget、仪表显示等设施依赖能力,厂商往往都会提供本人的一套接入 SDK,所以渠道分包势在必行
- 车机利用的交互要求简洁,突出重点,利用反对语音操作对于用户来说会是很大的吸引点
- 测试车机设备比拟不足
- 零碎版本跨度较大,目前接触到的设施从 Android 4.3 能够始终笼罩到 Android 10
- 性能个别较为羸弱,在开发时要分外留神性能的瓶颈
方案设计
针对上文中提到的车载独立 app 开发的一些特点,咱们在渠道分包、解耦车机依赖、语音操作接入、分辨率适配等方面进行了一系列摸索,上面介绍几个相干的方案设计
1. 多渠道接入能力形象
下面提到车机系统比拟碎片化,要实现车机的方控、桌面 widget、仪表显示等管制,个别有两种状况
- 厂商的相干操控实现了 Android 原生的 MediaSession 标准,这种状况下咱们要响应相干的 KeyEvent,并在各种播放相干机会调用 MediaSession api 更新状态
- 厂商为相干操控提供了 sdk 接入,这种状况下咱们要依照厂商自定义的标准来
思考到下层业务代码最好能不感知平台差别,决定对渠道接入能力做一层封装隔离
如上图所示,将渠道依赖的能力形象为 EnvironmentDependency 接口,不同渠道依赖各自的车机 sdk 实现该接口,Mediasession 标准独自实现一个通用类。业务层看到的是渠道无关的 DependcyWrapper 代理实例,只需在各业务解决机会调用代理的对应办法即可,躲避了业务层写渠道相干的代码。方控响应能力形象为 EventCallback 接口,业务实现后注入对应 dependcy 实例,由其适时触发。
针对分渠道打包问题,采纳 AGP 自带的 productFlavors 计划,不同的渠道蕴含不同的源文件夹,隔离 sdk 依赖。
flavorDimensions "channel"
productFlavors {
// 小鹏
xp {
dimension "channel"
buildConfigField("String", "channel", "\"xp\"")
}
// 比亚迪
byd {
dimension "channel"
buildConfigField("String", "channel", "\"byd\"")
}
......
}
2. 语音管制的设计实现
要做语音管制,首先须要思考如下问题
- 是利用本人实现还是应用车机能力?
从对接教训来看,目前提供车机语音凋谢能力的厂商并不广泛,个别厂商即便提供,其接入和自定义流程也比较复杂,须要相当长的周期,所以利用本人集成三方 sdk 来实现是更正当的抉择,然而针对于一些须要反对车机自带语音助手的厂商咱们也要提供出对应的计划 - 语音管制如何唤起?(除了页面点击外,是否提供其余快捷入口)
如果要实现特定短句唤起语音助手,就要求语音辨认 sdk 在利用生命周期内长期收音,始终抢占着 mic 焦点,导致车机系统自带的语音助手无奈工作(有个别车机实现了多麦克风阵列,即零碎收音应用独自 mic 通道,但这种车机是极少数),因而,短句唤起计划是行不通的。那么,是否借助方控呢?方控广泛能提供确认键的响应,如果利用业务自身不须要确认键(如利用为直播业务,不须要暂停、复原)则可间接应用确认键唤起语音助手,如果须要,也能够设计某种点按形式唤起(比方长按或者双击,这能够通过在业务层判断按键事件的工夫距离做到),当然,对应的疏导也须要跟上,比方在用户首次进入时展现浮层加语音的疏导 - 如何从语音辨认出的文字映射到对应操作?最不便的做法必定是客户端直接判断文字匹配性,比方辨认到“下一首”就切换到下一个直播,然而这种做法容错性较低,用户略微调整下说法就会生效,更加正当的做法是在语音转文字环节后再加上语义辨认环节,流程如下
解决了这些根本问题后,再来思考下一个比较完善的语音助手的残缺交互流程,助手唤起后,会首先进入询问态并提醒语音反对的操作类型,接着用户输出,如果输出超时会提醒助手行将敞开,失常输出后进行申请解析,获取后果后某些操作执行会间接敞开面板,而某些操作将间接在面板展现后果并回到询问态,若无奈解析则间接提醒并回到询问态,由此可见客户端上整个流程比拟适宜形象为一个状态机
- 如果须要对接不同的车机自带的语音助手,波及到管制相干的指令和播放信息的回调须要抽离出更为广泛的接口去实现,对于常见的指令,比方播放、暂停、上一首、下一首、珍藏、搜寻点播等须要封装成独立的办法,不同的车机的 app 注册不同的 server,客户端的实现则由同一个 client 解决,同时能将客户端解决后的后果返回给 server 端进行展现,这样做的益处是与车机对接的局部齐全交给 server 进行解决,client 只须要依据下发的指令进行对应的操作即可,前半部分是解耦的,后半局部是复用的
3. 多分辨率适配
前置的视觉交互设计中,思考到驾驶时的场景,罕用的操作区域要尽量放在凑近驾驶侧的一边,同时交互流程要尽可能简略,页面跳转层级不宜过多。除去支流的横屏布局之外,比亚迪、小鹏等车机屏幕也会存在竖屏的状况。
常见的屏幕适配计划包含 smallestWidth 适配、头条的批改 DisplayMetrics#density 计划、应用百分比布局等。联合我的项目的理论状况,咱们倡议大部分的布局都采纳流式布局,只须要在布局中扭转 recyclerView 的方向就能够适配横竖屏的切换,同时卡片布局尽量扁平化,ConstraintLayout 中的 Guideline、layout_constraintHeight_percent 等属性都能帮忙咱们很不便的实现百分比布局,如果遇到比例特地奇怪的屏幕,页面又不能应用流式布局时,能够思考联合 sw 限定符的计划,让视觉同学给出布局调整策略,独自针对大量非凡的屏幕进行适配。
在进行视觉适配开发时,咱们的第一反馈当然是让厂商提供所有可能波及的车机设备,然而这是不事实的,从咱们的对接教训来看,测试车机是相当紧缺的,局部厂商甚至连车机都临时无奈提供,只提供文档,让咱们自行适配后再内部测试。在这种状况下,咱们只能模仿不同的分辨率设施。adb shell wm size 命令就是解决办法,其承受 总长度像素值 x 总宽度像素值 格局的参数,运行后即可调整成对应的长宽比,测试过程只须要在同一设施上运行不同参数的命令即可实现不同分辨率的模仿。
性能优化
下面提到车机相比于手机,总体性能上要落后很多。在一开始,一方面因为历史包袱、组件复用等因素,另一方面编写代码时也往往疏忽了性能相干问题,使得 app 运行在车机上的体验相当蹩脚,装置慢、启动速度慢、卡顿丢帧等性能问题很显著的就裸露了进去, 于是咱们做了一系列针对性的优化
1. 减小包体积
减小包体积包含代码和资源两方面,通常的做法如下:
- 图片压缩
- 资源混同
- 缩小 Dex 数量
2. 缩小过程数
多过程运行须要占用更多的系统资源,在性能较弱的设施上,单 app 多过程的运行形式会给设施 CPU、内存等带来更多压力
3. 缩小线程数
和过程类似,线程过多在启动中频繁切换带来了很大的开销老本,主线程失去执行的工夫也会缩小
4. IO 优化
启动过程中文件 IO 过多也会拖慢启动速度,尽量减少不必要的文件读写
5. 缩小 Activity 的跳转次数
为了更快地展现界面或者执行某项具体性能,最好缩小启动流程中 Activity 的跳转层级,每多一个 Activity 就会减少几百毫秒的耗时;在申请一些接口时,也要思考到申请机会,是否能够前置并行申请,或者合并申请,缩小接口的 RT
6. 优化布局档次,缩小适度绘制
上面分享一个性能优化的实例,在与某家车厂的单干过程中,厂商反馈语音唤起阶段从冷启动到开始播放速度特地慢,将近 8s 之久。咱们在手机上测试是齐全没有问题的,然而受限于车机的性能,在前后重复数轮的沟通联调下,咱们次要做了以下优化
- 大幅缩小包体积,删除大量无用业务代码,包体积缩小约 80%,因包体积大幅减小,启动过程须要解压的 dex 数量也相应缩小,加载的类变少,速度有数秒晋升
- 将播放过程合入主过程,多过程改为单过程,并去除 aidl,去除 aidl 通信前后的几次文件读写,缩小约 2s 左右耗时
- 将 LoadingActivity 和首页 Activity 合并为一个,缩小启动链路过程 activity 的数量,缩小约数百毫秒耗时
- 将多个接口合并成一个缩小网络申请,缩小约 200 毫秒耗时
最终将工夫压缩到 3s 内,咱们的优化过程从后期对耗时显著局部着重优化,成果显著,到前期剖析启动日志,一点点抠细节,最终通过厂商方面的验收。在开始着手优化前,须要量化好具体指标,明确好指标再着手进行,用数据来掂量优化成果能让优化过程更加顺畅
踩坑指南
车载开发过程中,还遇到了一些之前手机利用开发不常见的问题,印象粗浅,也在这里分享下
1. 上了预装,RN 页面咋都不行了
车载场景,用户被动下载及更新 app 的频率绝对手机来说要低很多,所以预装是很重要的铺量伎俩,但当咱们好不容易与某渠道谈成预装后,却发现一个奇怪的问题,所有用 RN 实现的页面进入进入或者预加载就会引起利用的 crash,解体堆栈提醒的间接起因是 libjsexcutor.so 这个 RN 依赖的 js 解析库加载失败了,于是初步看了下 RN 解体地位的源码,发现 RN 的 so 库都是通过 SoLoader 这个 facebook 的工具加载的(官网文档说次要用来兼容 4.3 以下版本的 so 加载依赖问题),而利用中其余业务 so 的都是失常工作的,所以就猜想 SoLoader 在利用预装场景会存在问题,于是复现并重点查看 Soloader 相干的日志
上图为问题渠道上的 RN 加载日志,而下图为失常场景下的 RN 加载日志
能够看到两者的区别就在于问题渠道上,标红处的 so 查找门路没有被增加(该门路理论就是利用装置后的 so 门路的软链接),而失常渠道上是在该门路上找到了 RN 相干的 so 并进行了加载,顺着该思路查看了下 SoLoader 的源码,发现有如下逻辑
即判断以后利用是零碎利用后,就不将 app 默认 so 门路退出查找门路,导致 RN 相干用 Soloader 加载的库都会失败,定位到起因后,再认真过了下 SoLoader 加载 so 相干源码,发现其提供了 setSystemLoadLibraryWrapper 的设置接口,能够由下层来定义针对零碎利用场景如何加载依赖的 so,所以咱们只有设置该场景用利用本来的 so 加载形式即可解决问题,如下代码所示
SoLoader.setSystemLoadLibraryWrapper {ReLinker.loadLibrary(context, it)
}
2. 车机测试设施上的奇怪问题
-
某个渠道的测试车机连上公司 wifi 后,始终无法访问网络,与厂商沟通,他们告知也是首次提供测试车机给内部,外部应用是没问题的,于是只能本人定位。思考到大概率与网络环境无关,遂用 iptables 工具查看车机网络规定(iptables 是运行在用户空间的应用软件,通过管制 Linux 内核 netfilter 模块,来管理网络数据包的解决和转发,数据包的具体流转流程如下图所示,能够在各个环节减少规定来拦挡)
查看后果然发现局部规定比拟非凡,猜想是测试车机原本是只给厂商外部应用的,为了避免流出后产生问题,对网络环境做了辨认,一旦发现非厂商公司内网就抛弃数据包,于是用如下命令清理规定,问题解决iptables -F iptables -X iptables -P INPUT ACCEPT iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT
- 某个渠道的车机,开发过程发现局部接口报错。认真看了下,发现报错的接口都是 https 协定(开发阶段还在测试环境,大部分接口是 http 协定),adb 日志里看到的报错内容大抵如下
javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException:
Could not validate certificate: Certificate not valid until Wed Dec 16
09:00:05 GMT+08:00 2015 (compared to Sun Oct 12 16:20:03 GMT+08:00 1980)
看起来是工夫和证书有效期对不上,查看零碎工夫发现的确不对,原来该车机每次启动后都会重置零碎工夫,而 SSL 客户端的校验过程是蕴含证书有效期校验的,调整零碎工夫后即可解决问题
上述可见,测试车机会因为一些非凡设定而带来一些奇怪的开发问题,不过比拟好的一点是这些测试车机往往是曾经 root 过的,所以命令权限足够大,能够进行深刻地剖析。
技术之外的领会
参加车载利用从启动到正式上架的全过程,技术之外,还有一些其余的领会
- 车厂项目管理和互联网产品有较大区别,其风格比拟谨严粗疏,求稳不求快,没有互联网疾速迭代的理念,往往不太能承受局部问题先带上线后续迭代 fix 的做法,所以其测试周期通常比拟长,问题反馈轮次较多,反馈问题的角度也比拟多样(产品设计、内容经营、技术点),利用方须要有心理准备,急躁解决。
- 在与车厂初步沟通时,就要对齐好交付规范,比方适配的需要范畴、利用的性能指标等等,防止因为交付规范的不对立造成来回的沟通和返工,依据咱们本身的我的项目状况也要制订好本人的规范基线,平时通过 Monkey 和性能自动化测试保障 app 的稳定性
- 目前各车厂接入 app 的整体流程还不能说很欠缺,存在文档欠缺、测试车机欠缺、模拟器不稳固、反馈问题响应较慢等问题,这就要求利用方早做功课,对依赖项要尽早梳理,和厂商及时沟通,预知危险,前面随着利用接入越来越广泛,厂商这块的建设应该会有改良。
小结
本文介绍了目前车载开发的一些现状,分享了一些开发过程的设计思路和遇到的典型问题,心愿能对大家的利用上车有所帮忙!
本文公布自 网易云音乐大前端团队,文章未经受权禁止任何模式的转载。咱们长年招收前端、iOS、Android,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe(at)corp.netease.com!