背景
我的的产品作为一个海外音乐播映器,在车载场景听歌是一个很遍及的需求。在用户反响中,也有许多用户说到希望能在车上播映音乐。一起车载音乐也能够作为提高用户消费时长一个抓手。
出海产品,首要服务于海外用户。不同于国内的 Android 车载体系,往往是定制的 ROM,国外的 Android 车载也是 Google 一家独大,首要能够分为 Android Auto、Automotive OS。Android Auto 能够了解为是一套将手机运用「投屏」在车载屏幕上的方案,和 iOS 的 CarPlay 类似。而 Automotive OS 则类似于国内的定制 ROM。假如要支撑 Automotive OS,那咱们或许要重头开发一个适配大屏的 mini 版 app,那样作业量很大。所以咱们第一步先从支撑 Android Auto 开端
预备知识
Android Auto 支撑多种类型的运用,包括导航、媒体、音讯运用等。音乐播映器属于媒体运用,因而下面给出的参阅仅限于媒体运用。
Android Auto 的适配需求做提早了解做一些预备知识,具体如下:
-
官方文档
-
构建车载媒体运用
教你明白 Android Auto 开发媒体运用的各种概念以及怎么开发。这个网址的中文翻译可读性很差,主张直接看英文。别的,假如要开发的是导航类运用而不是媒体运用,那能够直接运用 androidX 的 Car Library 。
-
测验 Android Automotive OS 运用
教你怎么运用车机模拟器(DHU)合作手机上的 Android Auto app 来测验 Auto 运用。可是文档里有个坑点,文档里默许你的手机是 Pxiel 系列,Pxiel 系列是自带 Android Auto app 的,可是国产手机根本都没有这个 app,需求自己去下载然后装置在手机上。能够去这儿下载。别的 Android Auto app 第一次发动的时分会装置好几个谷歌服务,华为手机由于 ban 掉了google,这一步在华为手机上会失利。所以华为手机没法用来测验 Android Auto。推荐尽量运用 Pixel 系列手机来测验,防止测验过程中遇到奇奇怪怪的问题。
-
-
车载设备
- 业务功用的测验,一般运用模拟器 DHU 就能够。可是在运用发布之前,咱们肯定要在实在设备上充分测验。假如你有带 Android Auto 的真车的话,直接用真车测验就行。可是国内带 Android Auto 的车比较少,大概率是没有真车可供测验的。能够去闲鱼上独自购买一个车载主机(闲鱼查找「187b主机」即可)。
- 还有一个坑点,运用没有通过 google play store 分发的话,在实在车机设备上是不会显现的。这个问题官方文档没有明确说到,导致其时咱们 debug 的时分不可思议的花了很久的时间。
- 这儿顺带提一下,谷歌分发途径有四个,internal、closed testing、open testing、production,分别对应内部、内测、公测、产品。当然,咱们不或许为了测验直接提 play store 的 production 途径,用 internal 分发途径用作真机测验就能够了。closed testing 和 open testing 途径也能够。
-
质量标准
-
developer.android.google.cn/docs/qualit…
由于车载场景事关驾驶员生命安全,所以 google 对 Android Auto 运用审阅很严格。一切支撑 Android Auto 的运用,有必要满意上面链接中的质量标准才或许通过 Play Store 的审阅。这个标准要求的点许多,坑也不少,后边还会说到。这儿面很重要的一点是有必要支撑语音查找歌曲起播,这是审阅强卡的一个点。
-
-
官方 demo
- 由于文档讲得很不流畅,照着文档大概率是依然很难上手开发的。能够参阅 google 官方的音乐播映器 uamp,虽然这个 demo 很简单,可是它也支撑 Android Auto,有不懂的开发细节都能够参阅这个 demo。
-
Android Auto app(包名为 com.google.android.projection.gearhead)
- 想要把 android auto 连上车机跑起来,有必要要在手机上装置 Android Auto app,在这儿 下载。大部分国内的 rom 是不带这个 app 的,要自己手动装置。pixel 系列手机是自带的,能够在设置中查找到这个运用。
怎么开发
根本概念
首要,Android Auto 是不支撑自定义 UI 的,你的运用投屏到车机上,UI 展现现已在车机内部写死了,你能做的仅仅把数据传到车机上。所以支撑 Auto 的运用在车机上看起来都差不多。Auto 只允许你在播控界面的自定义操作里增加自定义图标。
其次,整个车机的播映流程涉及到三个部分,播映器运用、android auto app、车机。
别的还有几个概念咱们要了解
-
MediaBrowserService
- 指的便是音乐播映器的 Service。在 Service 里通过覆写
onGetRoot
、onLoadChildren
等办法对外供给车机所需求展现的数据。相当于「媒体生产者」,该生产者由你的 app 的供给。
- 指的便是音乐播映器的 Service。在 Service 里通过覆写
-
MediaBrowser
- 展现、消费你从上面的 MediaBrowserService 拿到的数据。手机上的 Android Auto app 内部包含了这个类。这个类会来主动 binds 上面说到的的 MediaBrowserService。相当于「媒体顾客」,便是 Android Auto app。
-
MediaSession
- MediaSession 便是 app 和车机之间进行交互的桥梁。实际上 app 和车机不是直接衔接进行通讯。app 是和 Android Auto app 进行 IPC 通讯,Android Auto 再和车机进行通讯。MediaSession 本质上便是一个对 IPC 的封装。这个封装不仅能和车机通讯,还能够和耳机线控等外部设备进行通讯。这些外部设备共用一个 MediaSession。
-
MediaItem、MediaMetadata、MediaDescription
- MediaItem 代表了车机屏幕上的一个媒体元素,比方某一个页面,一首歌,一张专辑等等,在这个类中对应的属性设置成什么,车机上屏幕上就显现什么。
- MediaMetadata 代表 MediaItem 中各个属性对应的值,用来结构 MediaDescription。MediaItem 持有 MediaDescription 用以描绘该媒体元素该怎么在车机上展现。
-
BROWSABLE 和 PLAYABLE
- 这个一个枚举符号,车机上每一个 MediaItem 要么是 BROWSABLE 的,要么是 PLAYABLE 的。
- PLAYABLE 意味着该 MediaItem 可播,这样当你在车机屏幕上点击该 MediaItem 时,会直接进行播映。这种 MediaItem 一般是歌曲,可直接起播。
- BROWSABLE 意味着该 MediaItem 是可浏览的,也便是说点击该 MediaItem 时,会加载一个新的媒体调集用于展现。比方专辑、歌单等,点击的时分会进入一个新的页面展现歌曲列表。
上面便是做 Android Auto 开发需求了解的根本的概念,能够了解为一套车机的框架,把手机和车机交互中的各个人物都定义好了。咱们的作业便是在这套框架上开发咱们的业务。
车机衔接手机
-
真车上和模拟器上都能够参阅官方文档 developer.android.com/training/ca…
-
或许能够下载这个压缩包,运转 main.py 即可。该脚本把上述文档里的操作步骤都集成进去了。github.com/ultimateHan…
-
连上今后,音乐主动会从车机上播映,像耳机连上手机之后音频主动从耳机播映相同。不需求额外在代码中设置。
检测衔接
- 这是 auto 开发中的一个坑点。以往有一个办法能够检测手机是否处在车载形式,可是该办法只对 android 12 以下的体系生效,android 12 上无效:
public static boolean isCarUiMode(Context c) {
UiModeManager uiModeManager = (UiModeManager) c.getSystemService(Context.UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
LogHelper.d(TAG, "Running in Car mode");
return true;
} else {
LogHelper.d(TAG, "Running on a non-Car mode");
return false;
}
}
-
官方有一个新的 CarLibrary 「androidx.car.app:app」,这个库中运用 CarConnection 类来检测车机和手机的衔接。
-
CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)
- 可是该办法只适合于 android 6-12 的体系,而且咱们只想运用衔接检测,导入整个库的话未免显得太粗笨。
-
-
根据上述 CarLibrary 「androidx.car.app:app」的办法,咱们把中心代码抽出来即可。具体代码我在 stackoverflow 上贴出来作为一个回答,也被采纳了。关于 android 5 的体系,运用前述的 isCarUiMode 办法即可。
stackoverflow.com/questions/3…
作业原理
如前面 MediaSession 的介绍所述,这个作业过程涉及到车机、android auto app、运用三方。为了叙述方便,后边说到的「车机回调客户端办法」实际上均是中介作用的 Android Auto App 通过 IPC 完结的。
下文中的 PlayerService 指的是运用中用于播映音乐的 Service。
全体的作业流程如下面的时序图所示,后边会逐个解说。
bindService
- 当咱们的车机和手机衔接上之后,Android Auto app 就会主动地 bind 咱们的 PlayerService。因而 Service 有必要声明
android:exported="true"
,这样才能够被外部拉起。一起整个 app 进程也被拉起了。
页面树
- 整个车机屏幕的 UI 能够看做一个页面树,客户端运用担任把定义各个页面树的节点 ID 和传输节点数据对应的 MediaItem。而车机在拿到这些数据今后就能够烘托屏幕 UI。
- 车机通过 IPC 回调到客户端代码中的 onGetRoot() 办法和 onLoadChildren() 办法来获取页面 ID 和数据。接下来具体说下这两个办法。
- 页面树的节点 ID 能够自己定义,只需保证每个节点对应仅有的 ID 即可。
- 一般来说,叶子节点是 PLAYABLE 类型的 MediaItem,而非叶子节点对应 BROWSABLE 类型的 MediaItem。
onGetRoot()
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): MediaBrowserServiceCompat.BrowserRoot
- onGetRoot 是车机和手机交互的入口办法,只需车机和手机衔接上了,就会回调这个办法。在这个办法中,需求结构一个 BrowserRoot 回来,作为车机页面树的根节点。整个页面树结构 BrowserRoot 需求传入 rootId。
- clientPackageName — 是引发客户端是包名,一般来说是 Android Auto App,包名是 com.google.android.projection.gearhead。可是有时分通过语音查找播歌引发客户端,包名便是 com.google.android.googlequicksearchbox,是 google 语音帮手。通过这个参数能够过滤你认为有用的包名。
- 其他两个参数一般用不上。
onLoadChildren()
override fun onLoadChildren(
parentMediaId: String,
result: Result<List<MediaItem>>
)
- onLoadChildren 办法一旦被回调,说明在车机上真实开端加载手机客户端对应的车机页面树了。所以这个办法能够认为是车机端的 launch 点,能够用这个办法统计车机 DAU。
- parentMediaId — 之前咱们会在 onGetRoot 中回来了 rootId,接下来车时机回调 onLoadChildren, rootId 就会作为该办法的 parentMediaId 传入,代表在此次 onLoadChildren 办法回调中咱们需求加载 parentMediaId 对应的页面树节点对应的 List,也便是页面树中该节点的子节点。
- 加载好了子节点后,以 List 的形式,通过 result.sendResult(List) 办法将成果回来给车机。
全体的页面树如上所示。当在车机上点击蓝色子节点时,会回调 onLoadChildren 加载下一级子节点。当点击绿色叶子节点时,会回调 onPlayFromMediaId 办法进行起播。下面具体介绍。
MediaSession.Callback
- MediaSession.Callback 是用户在车机上进行起播、播控以及语音查找等操作的回调。该抽象类中包含了一系列的回调办法。下面介绍一些重要的回调办法。
- 通过 mediaSession.setCallback 办法设置回调。
public abstract static class Callback {
public boolean onMediaButtonEvent(Intent mediaButtonEvent) {}// 线控耳机回调
public void onPlay() {} // 播映
public void onPause() {} // 暂停
public void onPlayFromMediaId(String mediaId, Bundle extras) {} // 起播
public void onPlayFromSearch(String query, Bundle extras) {} // 语音查找
public void onSkipToQueueItem(long id) {} // 切到播映行列中的某一首歌
public void onSkipToNext() {} // 切到下一首
public void onSkipToPrevious() {} // 切到上一首
public void onCustomAction(String action, Bundle extras) {} // 自定义操作
....
}
MediaSession.setPlaybackState
- 当咱们执行了上述回调后,怎么告诉车机进行播映页的 UI 更新呢?这时分就需求调用 MediaSession.setPlaybackState 办法来告诉车机更新 playback 的状态和 UI。PlaybackState,顾名思义,便是车机播映状态,所以改变这个状态意味着车机播控页 UI 也会更新。
- 该办法具体怎么运用能够参阅 UMAP demo 和官方文档,这儿不展开了。
onPlayFromMediaId
- 上面咱们说到,当用户在车机屏幕上点击歌曲起播时,就会回调该办法,并将构建页面树时赋予的 id 作为 mediaId 参数传入。因而,在该办法中咱们需求调用客户端的起播歌曲的办法来起播。
onPlay 和 onPause
- 这两个办法对应车机上的播映和暂停操作。
onSkipToNext 和 onSkipToPrevious
- 这两个办法对应车机上的切下一首和切上一首操作。
onSkipToQueueItem
- 在车机上切换到当前行列里的某一首歌,会回调该办法。传入对应的歌曲 id。
- 在行列切歌之前,需求先给车机结构行列。调用 mediaSessionCompat.setQueue(List) 即可。
onPlayFromSearch(String query, Bundle extras)
- 当咱们在车内运用语音帮手起播歌曲时,比方说 「播映周杰伦的夜曲」,会回调该办法。而且将语音内容辨认成文字,分词后将 「周杰伦 夜曲」 通过入参 query 传入。这样咱们拿到 query 字符串后,调用客户端的查找服务就能够获得查找的歌曲成果,并将该成果起播即可。
onCustomAction(String action, Bundle extras)
- 当你想在车机播控界面增加其他自定义的操作,比方保藏歌曲、单曲循环等,就会用到这个办法。
- 首要需求在结构 PlaybackState 的时分传入你定义的自定义操作,通过 PlaybackStateBuilder.addCustomAction(CustomAction action) 办法完结。结构 aciton 时需求传入仅有的标识字符串。在刷新了 PlaybackState 之后,在车机的播控界面就会呈现自定义的操作按钮。
- 当在车机屏幕点击了自定义操作按钮后,会回调 onCustomAction 办法,入参 action 便是仅有标识字符串,根据该字符串来区别不同的自定义操作。
开发中的坑
Android Auto 在国内渗透率不高,所以大部分开发者对这个东西很生疏,我也是。而且许多国产 ROM 体系层就不支撑 Android Auto。作为国内业界少量 Android Auto 的运用,在开发过程中阅历了资料匮乏、机型兼容、审阅被拒等许多坑。这儿把之前开发踩坑的阅历分享出来。
图标缓存
车机界面每个 tab 的 icon 在设置完之后是会有在车机里缓存的。假如修正了 icon 样式,必定要改掉对应的 drawable 的 id,不然车时机从缓存中取图片,icon 修正不生效。
机型不兼容
许多国产的 rom 对 Android Auto 的支撑有问题。具体表现有:
- 无法装置 GMS 导致无法运用 Android Auto App,以华为系手机为代表。
- 能够装置而且运转 Android Auto App,可是一旦连上 DHU 测验的时分,DHU 就一向黑屏,无法正常运转。亲测小米 11 、vivo S15 机型有该问题。
- 能够衔接 DHU,能够正常运转,可是运用 debug 包在测验的时分,DHU 上不会显现运用。只有运用 GP 商店分发的包才能显现出来。 部分 vivo 手机有这个问题。
所以,最好运用 pixel 这样的原生体系进行测验。
GP 分发
当咱们在 DHU 上测完,想运用实在车机进行测验的时分,却发现真车上不显现咱们的运用。正如之前所述,在真车上测验,需求通过 GP 分发的包才行,没有通过 GP 分发过的包,即使是 release 包也不行。这个坑其时困扰了咱们很久,最终咱们也是靠猜想才猜出原因。后来咱们和 google 官方进行沟通,也确认了这一点。可是坑爹的是,google 文档里完全没有提及这一点。
语音查找
语音查找这个功用,在 DHU 上常常不可思议地不好用。具体有
- 辨认不出语音,语音帮手回复 「对不起,我没有听懂」
- 辨认成别的运用,比方无论你怎么说 「运用 xxx 播映音乐」,它都回复 「好的,我来让 youtube music 播映音乐」,然后打开 youtube music 播映音乐
- 语音说完没有任何反响。比方你说 「播映音乐」,在语音帮手的对话框消失后就没有任何反响了,也没有回复,也不会打开运用播映。
- 辨认率差。咱们的运用名,常常会辨认错。可是像 Spotify、微信等运用名,辨认率很高。怀疑是 google 语音帮手对特定运用名辨认做了优化。 上面这些坑是在做语音查找功用时常常遇到的。QA 在测的过程中也会常常遇到而且反响 bug 给我。但大部分时分都是语音帮手抽风。
怎么判别到底是 bug 还是语音帮手抽风呢,能够用相同的语音去试下其他运用,比方 spotify 和 YT music。假如也有相同的问题,那么能够认为是语音帮手又抽风了。
审阅
Google 商店对车载运用的审阅标准很高。详见 质量标准,其中对车载运用需求满意什么样的条件做了严格的要求。关于音乐类运用,有几点简单忽视的需求格外重视:
- 有必要支撑语音查找播歌功用。google 认为,用户在开车时不能分散注意力,所以有必要供给语音查找播歌的功用,让用户能够开车的一起按下方向盘上的麦克风按钮,直接语音操控歌曲的播映。假如这个功用没满意,运用不能过审。
- 歌曲播映时,假如手机上碰到堵塞,比方出需求手动关闭的广告、出弹窗、请求权限,有必要让用户转到手机上处理时,这时分有必要要在车机屏幕上提示用户。这时能够运用过错提示的 API 来做提示。
-
增加特定的 Intent Filter
-
<activity ...> <intent-filter> <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
必定要在发动 Acitivity 里边增加这个 intent-filter,用来兼容陈旧版本的 android 手机语音播歌。能够参阅 developer.android.com/guide/compo… 。
这个点其实在 google 官方文档里有提,可是没有明确说有必要要有,仅仅主张增加。可是假如不加的话,运用审阅会被拒绝。所以这儿也是个坑点。
-