构建 Android Auto 导航、停车和充电应用

Android for Cars 应用库可以帮助您实现汽车导航、停车和充电应用。为此,它提供了一套模板,这些模板符合防止驾驶员分心的标准,它还解决了一些细节问题,例如存在各种车载显示屏类型和输入模式。

本指南简要介绍了该库的关键功能和概念,并引导您逐步完成设置简单应用的过程。

准备工作

  1. 查看 Android for Cars 应用库设计准则
  2. 查看本部分中列出的关键术语和概念
  3. 熟悉 Android Auto 系统界面
  4. 查看版本说明
  5. 查看示例
  6. [仅适用于闭源库] 查看 Android for Cars 应用库使用条款

关键术语和概念

模型和模板
界面由模型对象的图来表示,这些模型对象可以按照它们所属的模板允许的不同方式排列在一起。模板是模型的子集,它们可以在这些图中充当根。模型包含要以文字和图片的形式显示给用户的信息,以及用于配置此类信息的视觉外观各个方面(例如,文字颜色或图片大小)的属性。主机会将模型转换为符合防止驾驶员分心标准的视图,还解决了一些细节问题,例如存在各种车载显示屏类型和输入模式。
主机
主机是一个后端组件,它会实现库的 API 提供的功能,以便您的应用在汽车中运行。从发现应用并管理其生命周期,到将模型转换为视图,再到将用户交互操作通知给应用,这些都属于主机的职责范围。在移动设备上,此主机由 Android Auto 实现。
模板限制
不同的模板会对其模型的内容施加限制。例如,列表模板对可以呈现给用户的项数有限制。模板对可以采用什么方式连接它们以形成任务流也有限制。例如,应用最多只能将 5 个模板推送到屏幕堆栈。如需了解详情,请参阅模板限制
Screen
Screen 是一个由库提供的类,应用实现该类来管理呈现给用户的界面。Screen 具有 lifecycle,并提供了一种机制,可让应用发送要在屏幕可见时显示的模板。此外,也可以将 Screen 实例推送到屏幕堆栈以及从屏幕堆栈中弹出这些实例,这样可以确保它们遵循模板流限制
CarAppService
CarAppService 是一个抽象 Service 类,应用必须实现并导出该类才能被主机发现并由主机进行管理。应用的 CarAppService 负责使用 CarAppService.createHostValidator 验证主机连接是否可以信任,随后使用 CarAppService.onCreateSession 为每个连接提供 Session 实例。
Session
Session 是一个抽象类,应用必须使用 CarAppService.onCreateSession 实现并返回该类。它充当在车载显示屏上显示信息的入口点,并且具有生命周期,可告知车载显示屏上应用的当前状态,例如当应用可见或隐藏时。

Session 开始时(例如当应用首次启动时),主机会使用 Session.onCreateScreen 方法请求要显示的初始 Screen

安装库

有关如何将库添加到应用的说明,请参阅 Jetpack 库发布页面

配置应用的清单文件

您需要先配置应用的清单文件,然后才能创建汽车应用。

声明 CarAppService

主机通过 CarAppService 实现连接到您的应用。您应在清单中声明此服务,以允许主机发现并连接到您的应用。

您还需要在应用的 intent 过滤器的 category 元素中声明应用的类别。请查看支持的应用类别的列表,了解此元素允许的值。

以下代码段展示了如何在清单中声明停车应用的汽车应用服务:

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService"/>
        <category android:name="androidx.car.app.category.PARKING"/>
      </intent-filter>
    </service>

    ...
<application>

支持的应用类别

为了能够在 Play 商店中的 Android Auto 版块上架,应用需要属于某个支持的汽车应用类别。声明汽车应用服务时,您可以通过在 intent 过滤器中添加以下一个或多个支持的类别值来声明应用的类别:

  • androidx.car.app.category.NAVIGATION:此类应用提供精细导航方向。
  • androidx.car.app.category.PARKING:此类应用提供与查找停车位相关的功能。
  • androidx.car.app.category.CHARGING:此类应用提供与查找电动车辆充电站相关的功能。

有关应用属于各个类别的详细说明和条件,请参阅 Android 汽车应用质量

指定应用名称和图标

您需要指定应用名称和图标,主机可以使用它们在系统界面中表示您的应用。

您可以使用 CarAppServicelabelicon 元素来指定用于表示应用的应用名称和图标:

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

如果未在 service 元素中声明标签或图标,主机将回退到使用为应用指定的值。

设置应用的 minSdkVersion

Android Auto 要求您的应用以 Android 6.0(API 级别 23)或更高版本为目标平台。

如需在项目中指定此值,请在手机应用模块的 AndroidManifest.xml 文件中将 uses-sdk 元素中的 minSdkVersion 属性设置为 23 或更高版本,如以下示例所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
    ...
</manifest>

声明 Android Auto 支持

Android Auto 主机会检查应用是否已声明支持 Android Auto。如需启用此支持,请在应用的清单中添加以下条目:

<application>
    ...
    <meta-data
        android:name="com.google.android.gms.car.application"
        android:resource="@xml/automotive_app_desc"/>
    ...
</application>

此清单条目引用您应创建的另一个 XML 文件,其路径为 YourAppProject/app/src/main/res/xml/automotive_app_desc.xml,该文件中声明了应用支持的 Android Auto 功能。

使用 Android for Cars 应用库的应用必须在 automotive_app_desc.xml 文件中声明 template 功能:

<automotiveApp>
    <uses name="template" />
</automotiveApp>

创建 CarAppService 和 Session

您的应用需要扩展 CarAppService 类并实现 CarAppService.onCreateSession 方法,该方法会返回一个 Session 实例,它对应于到主机的当前连接:

public final class HelloWorldService extends CarAppService {
  ...
  @Override
  @NonNull
  public Session onCreateSession() {
    return new HelloWorldSession();
  }
  ...
}

Session 实例负责返回要在应用首次启动时使用的 Screen 实例:

public final class HelloWorldSession extends Session {
  ...
  @Override
  @NonNull
  public Screen onCreateScreen(@NonNull Intent intent) {
    return new HelloWorldScreen();
  }
  ...
}

若要处理汽车应用需要从应用主屏幕或着陆屏幕以外的屏幕启动的情况(例如处理深层链接),您可以使用 ScreenManager.push,在应用从 onCreateScreen 返回前预先植入屏幕的返回堆栈。预先植入可让用户从应用显示的第一个屏幕导航回之前的屏幕。

创建启动屏幕

您可以通过定义扩展 Screen 类的类并实现 Screen.onGetTemplate 方法来创建由应用显示的屏幕,该方法会返回 Template 实例,它表示要在车载显示屏上显示的界面状态。

以下代码段展示了如何声明 Screen,它使用 PaneTemplate 模板显示简单的“Hello world!”字符串:

public class HelloWorldScreen extends Screen {
  @NonNull
  @Override
  public Template onGetTemplate() {
    Pane pane = new Pane.Builder()
        .addRow(new Row.Builder()
            .setTitle("Hello world!")
            .build())
        .build();
    return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
  }
}

CarContext 类

CarContext 类是可由 SessionScreen 实例访问的 ContextWrapper 子类,它可提供对汽车服务的访问,例如用于管理屏幕堆栈ScreenManager、用于常规应用相关功能(例如访问 Surface 对象以绘制导航应用的地图)的 AppManager,以及精细导航应用就导航元数据及其他导航相关事件与主机通信所用的 NavigationManager。如需查看导航应用可用的库功能的详尽列表,请参阅访问导航模板部分。

CarContext 还提供了一些其他功能,例如允许从车载显示屏使用配置加载可绘制资源、使用 intent 在汽车中启动应用,以及指示导航应用是否应在深色模式下显示其地图。

实现屏幕导航

应用通常会呈现许多不同的屏幕,每个屏幕可能会利用不同的模板,用户可以在与屏幕中显示的界面交互时浏览这些屏幕。

ScreenManager 类提供了一个屏幕堆栈,您可以使用它来推送屏幕,当用户选择车载显示屏上的返回按钮或使用某些汽车中提供的硬件返回按钮时,可以自动弹出这些屏幕。

以下代码段展示了如何向消息模板添加返回操作,以及在用户选择新屏幕时推入该屏幕的操作:

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(() -> getScreenManager().push(new NextScreen()))
            .build())
    .build();

Action.BACK 对象是自动调用 ScreenManager.pop 的标准 Action。可通过使用 CarContext 提供的 OnBackPressedDispatcher 实例来替换此行为。

为了确保应用在汽车行驶过程中能够保障安全,屏幕堆栈的最大深度为 5 个屏幕。如需了解详情,请参阅模板限制

刷新模板的内容

应用可通过调用 Screen.invalidate 方法来请求使 Screen 的内容无效。主机随后回调应用的 Screen.onGetTemplate 方法,以检索包含新内容的模板。

刷新 Screen 时,请务必了解模板中可更新的特定内容,以便主机不会将新模板计入模板配额。如需了解详情,请参阅模板限制

建议您为屏幕设置适当的结构,以使 Screen 与其通过 Screen.onGetTemplate 实现返回的模板类型之间存在一对一的映射关系。

处理用户输入

应用可通过将适当的监听器传递给支持它们的模型来响应用户输入。以下代码段展示了如何创建一个 Action 模型,该模型设置了一个 OnClickListener,它会回调由应用代码定义的方法:

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

然后,onClickNavigate 方法可使用 CarContext.startCarApp 方法启动默认的汽车导航应用

private void onClickNavigate() {
  Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
  getCarContext().startCarApp(intent);
}

如需详细了解如何启动应用(包括 ACTION_NAVIGATE intent 的格式),请参阅使用 intent 启动汽车应用

某些操作(例如那些需要引导用户在其移动设备上继续交互的操作)只有在汽车停好后才允许执行。您可以使用 ParkedOnlyOnClickListener 实现这些操作。如果汽车没有停好,主机会向用户显示一条消息,指出在这种情况下不允许执行该操作。如果汽车已停好,代码就会正常执行。以下代码段展示了如何使用 ParkedOnlyOnClickListener 在移动设备上打开设置屏幕:

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(
        ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)
    .build();

显示通知

发送到移动设备的通知只有在使用 CarAppExtender 扩展后才会显示在车载显示屏上。某些通知属性(例如内容标题、文字、图标和操作)可以在 CarAppExtender 中设置,从而在通知显示在车载显示屏上时替换其属性。

以下代码段展示了如何向车载显示屏发送一条通知,让其显示的标题不同于移动设备上显示的标题:

Notification notification =
    new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
        .setContentTitle(titleOnThePhone)
        .extend(
            new CarAppExtender.Builder()
                .setContentTitle(titleOnTheCar)
                ...
                .build())
        .build();

通知可能会影响界面的以下几个部分:

  • 可能会向用户显示浮动通知 (HUN)。
  • 可能会在通知中心添加一个条目,并且选择性地在侧边栏显示一个标志。
  • 对于导航应用,通知可能会显示在侧边栏微件中,如精细导航通知中所述。

应用可以通过使用通知的优先级,选择如何配置通知以影响每个界面元素,如 CarAppExtender 文档中所述。

如果调用 NotificationCompat.Builder.setOnlyAlertOnce 且将值设置为 true,则高优先级通知将只显示为 HUN 一次。

如需详细了解如何设计汽车应用的通知,请参阅通知

显示消息框

应用可以使用 CarToast 显示消息框,如以下代码段所示:

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

使用 intent 启动汽车应用

您可以调用 CarContext.startCarApp 方法来执行以下某项操作:

  • 打开拨号器拨打电话。
  • 使用默认汽车导航应用开始精细导航到某个位置。
  • 使用 intent 启动您自己的应用。

以下示例展示了如何创建一条通知,该通知包含一项操作,即打开应用中显示停车预订详情的屏幕。您可以使用内容 intent 扩展通知实例,该 intent 包含 PendingIntent,它将显式 intent 封装到应用的操作中:

Notification notification =
    notificationBuilder.
        …
        .extend(
            new CarAppExtender.Builder()
                .setContentIntent(
                    PendingIntent.getBroadcast(
                        context,
                        ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                        new Intent(ACTION_VIEW_PARKING_RESERVATION)
                            .setComponent(
                                new ComponentName(context, MyNotificationReceiver.class)),
                        0))
                .build())

应用还必须声明 BroadcastReceiver,当用户在通知界面中选择相应的操作并使用包含数据 URI 的 intent 调用 CarContext.startCarApp 时,会调用该类来处理 intent:

public class MyNotificationReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    String intentAction = intent.getAction();
    if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) {
      CarContext.startCarApp(
         intent,
          new Intent(Intent.ACTION_VIEW)
              .setComponent(new ComponentName(context, MyCarAppService.class))
              .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)));
    }
  }
}

最后,应用中的 Session.onNewIntent 方法通过在堆栈上推入停车预订屏幕(如果还没有在顶部)来处理此 intent:

@Override
public void onNewIntent(@NonNull Intent intent) {
  Uri uri = intent.getData();
  if (uri != null
      && MY_URI_SCHEME.equals(uri.getScheme())
      && MY_URI_HOST.equals(uri.getSchemeSpecificPart())
      && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment())) {

    Screen top = screenManager.getTop();
    if (!(top instanceof ParkingReservationScreen)) {
      ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
      screenManager.push(new ParkingReservationScreen(getCarContext()));
    }
  }
}

如需详细了解如何处理汽车应用的通知,请参阅显示通知

模板限制

主机将针对给定任务显示的模板数限制为最多 5 个,在这 5 个模板中,最后一个模板必须是以下某种类型:

  1. NavigationTemplate
  2. PaneTemplate
  3. MessageTemplate

请注意,此限制适用于模板数,而不是堆栈中的 Screen 实例数。例如,如果在屏幕 A 中,应用发送了 2 个模板,然后推送屏幕 B,那么它现在可以再发送 3 个模板。或者,如果将每个屏幕的结构都设置为发送单个模板,那么应用可以将 5 个屏幕实例推送到 ScreenManager 堆栈上。

这些限制有一些特殊情况:模板刷新、返回和重置操作。

模板刷新

某些内容更新不计入模板限制。一般来说,只要应用推送的新模板所属的类型及其包含的主要内容与之前的模板相同,就不会将新模板计入配额。例如,更新 ListTemplate 中某一行的切换状态不会计入配额。如需详细了解可将哪些类型的内容更新视为刷新,请参阅各个模板的文档。

返回操作

为了在任务中启用子流,主机会检测应用何时从 ScreenManager 堆栈中弹出 Screen,并根据应用倒退的模板数更新剩余配额。

例如,如果在屏幕 A 中,应用发送了 2 个模板,然后推送屏幕 B 并且又发送了 2 个模板,那么应用的剩余配额就为 1。如果应用现在弹回到屏幕 A,主机会将配额重置为 3,因为应用倒退了 2 个模板。

请注意,当弹回到某个屏幕时,应用发送的模板所属的类型必须与该屏幕上次发送的模板的类型相同。发送其他任何类型的模板都会导致错误。不过,只要类型在返回操作期间保持不变,应用就可以随意修改模板的内容,而不会影响配额。

重置操作

某些模板具有表示任务结束的特殊语义。例如,NavigationTemplate 是一个视图,它应该会持续显示在屏幕上,并使用新的精细导航指示进行刷新,以供用户使用。到达其中一个模板时,主机会重置模板配额,将该模板当作新任务的第一步来对待,从而使应用能够开始新任务。如需了解哪些模板会在主机上触发重置操作,请参阅各个模板的文档。

如果主机收到通过通知操作或从启动器启动应用的 intent,也会重置配额。此机制使应用能够从通知开始新任务流,即使应用已绑定且在前台运行,也是如此。

如需详细了解如何在车载显示屏上显示应用的通知,请参阅显示通知;如需了解如何通过通知操作启动应用,请参阅使用 intent 启动汽车应用

构建停车或充电应用

本部分详细介绍您可以使用库的哪些不同功能来实现停车或充电应用的功能。

在清单中声明类别支持

应用需要在其 CarAppService 的 intent 过滤器中声明 androidx.car.app.category.PARKINGandroidx.car.app.category.CHARGING 汽车应用类别。例如:

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.PARKING"/>
      </intent-filter>
    </service>
    ...
<application>

访问地图模板

应用可以访问 PlaceListMapTemplate,该模板专门用于在由主机渲染的地图上显示一系列感兴趣的地点。

为了能够访问此模板,应用需要在其 AndroidManifest.xml 中声明 androidx.car.app.MAP_TEMPLATES 权限:

<uses-permission android:name="androidx.car.app.MAP_TEMPLATES"/>

构建导航应用

本部分详细介绍您可以使用库的哪些不同功能来实现精细导航应用的功能。

在清单中声明导航支持

导航应用需要在其 CarAppService 的 intent 过滤器中声明 androidx.car.app.category.NAVIGATION 汽车应用类别

<application>
    ...
   <service
       ...
        android:name=".MyNavigationCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.NAVIGATION"/>
      </intent-filter>
    </service>
    ...
<application>

支持导航 intent

为了支持发送到应用的导航 intent(包括来自 Google 助理的语音查询导航 intent),应用需要在其 Session.onCreateScreenSession.onNewIntent 中处理 CarContext.ACTION_NAVIGATE intent。

如需详细了解该 intent 的格式,请参阅 CarContext.startCarApp 的文档。

访问导航模板

导航应用可以访问专为导航应用设计的以下模板。所有这些模板都会在后台显示一个 Surface,应用可以访问该 Surface 以便绘制地图,这些模板还会显示应用提供的其他信息,这些信息因模板而异。

如需详细了解如何使用这些模板设计导航应用的界面,请参阅 Android for Cars 应用库设计准则

为了能够访问导航模板,应用需要在其 AndroidManifest.xml 中声明 androidx.car.app.NAVIGATION_TEMPLATES 权限:

<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>

绘制地图

导航应用可以访问 Surface,以在相关模板上绘制地图。

然后,可以通过将 SurfaceCallback 实例设置为 AppManager 汽车服务来访问 SurfaceContainer 对象:

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

SurfaceContainer 可用时,SurfaceCallback 会提供一个回调;当 Surface 的属性发生更改时,它还会提供其他回调。

为了能够访问 Surface,应用需要在其 AndroidManifest.xml 中声明 androidx.car.app.ACCESS_SURFACE 权限:

<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>

地图的可见区域

主机可能会在地图上为不同的模板绘制界面元素。主机将通过调用 SurfaceCallback.onVisibleAreaChanged 来告知保证不被遮挡而完全对用户可见的区域。此外,为了最大限度地减少更改次数,主机还会根据当前模板使用将会可见的最大矩形来调用 SurfaceCallback.onStableAreaChanged 方法。

例如,如果导航应用使用的是顶部带有操作栏的 NavigationTemplate,当用户有一段时间没有与屏幕交互时,该操作栏可能会隐藏自身,以便为地图腾出更多空间。在这种情况下,将使用相同的矩形对 onStableAreaChangedonVisibleAreaChanged 进行回调。当操作栏处于隐藏状态时,仅使用较大的区域调用 onVisibleAreaChanged。如果用户与屏幕交互,则同样仅使用第一个矩形调用 onVisibleAreaChanged

深色模式

当主机确定条件允许时,导航应用必须使用适当的深色将地图重新绘制到 Surface 实例上,如 Android Auto 应用质量准则中所述。

为了决定是否应绘制深色地图,您可以使用 CarContext.isDarkMode 方法。每当深色模式状态发生变化时,您都会收到对 Session.onCarConfigurationChanged 的调用。

导航应用必须就额外的导航元数据与主机通信。主机利用这些信息向车机提供信息,并防止导航应用在共享资源上发生冲突。

导航元数据通过可从 CarContext 访问的 NavigationManager 汽车服务提供:

NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
开始、结束和停止导航

为了让主机管理多个导航应用、路线通知和车辆仪表板数据,它需要了解导航的当前状态。当用户开始导航时,应用应调用 NavigationManager.navigationStarted。同样,当导航结束时,例如当用户到达目的地或用户取消导航时,应用应调用 NavigationManager.navigationEnded

只有在用户完成导航时,您才能调用 NavigationManager.navigationEnded。例如,如果您需要在行程中间重新计算路线,请改用 Trip.Builder.setLoading(true)

有时,主机需要应用停止导航,并将在应用通过 NavigationManager.setListener 提供的 NavigationManagerListener 对象中调用 stopNavigation。然后,应用必须停止在仪表板屏幕、导航通知和语音导航中发出下一个转弯的信息。

行程信息

在有效导航期间,应用应调用 NavigationManager.updateTrip。此调用中提供的信息将用于车辆的仪表板和平视显示仪。并非所有信息都可以显示给用户,具体取决于驾驶的特定车辆。

为了测试信息是否到达仪表板,可以配置桌面车机 (DHU) 工具以显示简单的仪表板屏幕。创建一个包含以下内容的 cluster.ini 文件:

[general]
instrumentcluster = true

然后,您可以使用一个额外的命令行参数调用 DHU:

dhu -c cluster.ini

精细导航通知

精细导航 (TBT) 指示可以通过频繁更新的导航通知来提供。为了在车载显示屏中被视为导航通知,通知的构建器必须执行以下操作:

  1. 使用 NotificationCompat.Builder.setOngoing 方法将通知标记为持续性通知。
  2. 将通知的类别设置为 Notification.CATEGORY_NAVIGATION
  3. 使用 CarAppExtender 扩展通知。

导航通知将显示在车载显示屏底部的侧边栏微件中。如果通知的重要性级别设置为 IMPORTANCE_HIGH,它也会显示为浮动通知 (HUN)。如果未使用 CarAppExtender.Builder.setImportance 方法设置重要性,将采用通知渠道的重要性

应用可以在 CarAppExtender 中设置 PendingIntent,当用户点按 HUN 或侧边栏微件时,会将其发送到应用。

如果调用 NotificationCompat.Builder.setOnlyAlertOnce 且将值设置为 true,则高重要性通知将只以 HUN 的形式提醒一次。

以下代码段展示了如何构建导航通知:

new NotificationCompat.Builder(context, myNotificationChannelId)
    ...
    .setOnlyAlertOnce(true)
    .setOngoing(true)
    .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(carScreenTitle)
            ...
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_OPEN_APP.hashCode(),
                    new Intent(ACTION_OPEN_APP)
                        .setComponent(
                            new ComponentName(context, MyNotificationReceiver.class)),
                    0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
    .build())
精细导航通知的准则

导航应用应随着距离的变化定期更新 TBT 通知,这样会更新侧边栏微件,并且只将通知显示为 HUN。应用可使用 CarAppExtender.Builder.setImportance 方法设置通知的重要性,由此控制 HUN 行为。将重要性设置为 IMPORTANCE_HIGH 将显示 HUN,将其设置为其他任何值都将只更新侧边栏微件。

语音导航

如需通过汽车扬声器播放导航指导,应用必须请求音频焦点。作为 AudioFocusRequest 的一部分,您应将用法设置为 AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE。您还应将焦点获取设置为 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

模拟导航

为了在将应用提交到 Google Play 商店时验证其导航功能,应用必须实现 NavigationManagerCallback.onAutoDriveEnabled 回调。如果调用了此回调,当用户开始导航时,应用应模拟导航到选定的目的地。只要当前 Session 的生命周期达到 Lifecycle.Event#ON_DESTROY 状态,应用就可以退出此模式。

您可以通过从命令行执行以下命令来测试是否调用了 onAutoDriveEnabled 实现:

adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE

例如:

adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE

默认汽车导航应用

在 Android Auto 中,默认汽车导航应用为用户最近启动的导航应用。例如,当用户通过助理调用导航命令或其他应用发送 intent 以开始导航时,则就是接收导航 intent 的应用。

CarAppService、Session 和 Screen 的生命周期

SessionScreen 类实现了 LifecycleOwner 接口。当用户与应用交互时,系统将调用 SessionScreen 对象的生命周期回调,如下图所示。

CarAppService 和 Session 的生命周期

图 1. Session 生命周期。

如需了解完整详情,请参阅 Session.getLifecycle 方法的文档。

Screen 的生命周期

图 2. Screen 生命周期。

如需了解完整详情,请参阅 Screen.getLifecycle 的文档。

测试库

Android for Cars 测试库提供了一些辅助类,可用于在测试环境中验证应用的行为。例如,借助 SessionController 可以模拟与主机的连接,验证是否创建并返回正确的 ScreenTemplate

请参阅示例,查看使用示例。

在真实的车机上运行应用

为了让应用在真实的车机(而不是我们提供的桌面车机)上运行,必须通过 Google Play 商店来分发应用。这样可确保应用经过测试和审查,遵循我们的准则。这些准则可确保应用与汽车环境相关,并通过我们的防止驾驶员分心测试。

在开发过程中进行测试时,有三个选项可供选择:

  • 使用桌面车机。
  • 将应用推送到 Google Play 商店的 internal test track。内部测试轨道允许您手动添加团队,以便进行内部测试。发布到此轨道不需要 Play 商店审核。
  • 通过 Google Play 管理中心中的 internal app sharing 共享您的应用。与内部测试轨道类似,这不需要 Play 商店审核。

每当您将 APK 发布到其他任何轨道(包括封闭式轨道)时,应用都会先经历审核流程,然后才被批准进入 Play 商店中的该轨道。如果应用未能通过审核流程,您将收到有关为什么未通过的信息。此流程使您能够修复所有问题,以便符合我们的准则。

报告 Android for Cars 应用库问题

如果您发现该库存在问题,请使用 Google 问题跟踪器报告该问题。请务必在问题模板中填写所有必填信息。

创建新问题

在提交新问题之前,请先查看该问题是否已在库的版本说明中列出或在问题列表中报告。您可以在跟踪器中点击问题的星标来对问题进行订阅和投票。如需了解详情,请参阅订阅问题