背景

我的的产品作为一个海外音乐播映器,在车载场景听歌是一个很遍及的需求。在用户反响中,也有许多用户说到希望能在车上播映音乐。一起车载音乐也能够作为提高用户消费时长一个抓手。

出海产品,首要服务于海外用户。不同于国内的 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 支撑多种类型的运用,包括导航、媒体、音讯运用等。音乐播映器属于媒体运用,因而下面给出的参阅仅限于媒体运用。

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 开发指北

怎么开发

根本概念

首要,Android Auto 是不支撑自定义 UI 的,你的运用投屏到车机上,UI 展现现已在车机内部写死了,你能做的仅仅把数据传到车机上。所以支撑 Auto 的运用在车机上看起来都差不多。Auto 只允许你在播控界面的自定义操作里增加自定义图标。

其次,整个车机的播映流程涉及到三个部分,播映器运用、android auto app、车机。

别的还有几个概念咱们要了解

  • MediaBrowserService

    • 指的便是音乐播映器的 Service。在 Service 里通过覆写 onGetRootonLoadChildren 等办法对外供给车机所需求展现的数据。相当于「媒体生产者」,该生产者由你的 app 的供给。
  • MediaBrowser

    • 展现、消费你从上面的 MediaBrowserService 拿到的数据。手机上的 Android Auto app 内部包含了这个类。这个类会来主动 binds 上面说到的的 MediaBrowserService。相当于「媒体顾客」,便是 Android Auto app。
  • MediaSession

    • MediaSession 便是 app 和车机之间进行交互的桥梁。实际上 app 和车机不是直接衔接进行通讯。app 是和 Android Auto app 进行 IPC 通讯,Android Auto 再和车机进行通讯。MediaSession 本质上便是一个对 IPC 的封装。这个封装不仅能和车机通讯,还能够和耳机线控等外部设备进行通讯。这些外部设备共用一个 MediaSession。

Android Auto 开发指北

  • 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。

全体的作业流程如下面的时序图所示,后边会逐个解说。

Android Auto 开发指北

bindService
  • 当咱们的车机和手机衔接上之后,Android Auto app 就会主动地 bind 咱们的 PlayerService。因而 Service 有必要声明 android:exported="true" ,这样才能够被外部拉起。一起整个 app 进程也被拉起了。

Android Auto 开发指北

页面树
  • 整个车机屏幕的 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) 办法将成果回来给车机。

Android Auto 开发指北

全体的页面树如上所示。当在车机上点击蓝色子节点时,会回调 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 认为,用户在开车时不能分散注意力,所以有必要供给语音查找播歌的功用,让用户能够开车的一起按下方向盘上的麦克风按钮,直接语音操控歌曲的播映。假如这个功用没满意,运用不能过审。

Android Auto 开发指北

  • 歌曲播映时,假如手机上碰到堵塞,比方出需求手动关闭的广告、出弹窗、请求权限,有必要让用户转到手机上处理时,这时分有必要要在车机屏幕上提示用户。这时能够运用过错提示的 API 来做提示。

Android Auto 开发指北

Android Auto 开发指北

  • 增加特定的 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 官方文档里有提,可是没有明确说有必要要有,仅仅主张增加。可是假如不加的话,运用审阅会被拒绝。所以这儿也是个坑点。