Android 版 Driver SDK 使用入门

您可以使用驱动程序 SDK 提供增强的导航和跟踪功能 行程和订单进度应用驱动程序 SDK 提供 对按需乘车和送货解决方案舰队引擎的更新车辆位置和任务。

驱动程序 SDK 可让 Fleet Engine 服务和您的自定义服务保持感知 车辆的位置和状态信息。例如,车辆可以是 ONLINEOFFLINE,且车辆位置会随着行程的进行而发生变化。

最低系统要求

移动设备必须搭载 Android 6.0 (API 级别 23)或更高版本。

构建和依赖项配置

Google Maven 制品库中提供驱动程序 SDK 4.99 版及更高版本。

Gradle

请将以下内容添加到 build.gradle 文件:

repositories {
    ...
    google()
}

Maven

请将以下内容添加到 pom.xml 文件:

<project>
  ...
  <repositories>
    <repository>
      <id>google-maven-repository</id>
      <url>https://maven.google.com</url>
    </repository>
  </repositories>
  ...
</project>

项目配置

如需使用驱动程序 SDK,您的应用必须 minSdkVersion 23 或更高版本。

要运行使用驱动程序 SDK 构建的应用,Android 设备必须具有 Google Play 服务 已安装。

设置您的开发项目

设置您的开发项目并获取 API 密钥 在 Google Cloud 控制台中为项目指定权限:

  1. 创建新的 Google Cloud 控制台项目,或者选择一个现有项目以供使用 驱动程序 SDK。等待几分钟,直到 新项目即会显示在 Google Cloud 控制台中。

  2. 为了运行演示版应用,您的项目必须有权访问 Maps SDK 。在 Google Cloud 控制台中,选择 API 和服务 >库,然后针对 Android。

  3. 通过以下方式获取项目的 API 密钥: API 和服务 >凭据 >创建凭据 >API 密钥。 如需详细了解如何获取 API 密钥,请参阅 获取 API 密钥

将驱动程序 SDK 添加到您的应用

驱动程序 SDK 可从 Google Maven 制品库中获取。通过 代码库包括 SDK 的项目对象模型 (.pom) 文件和 Javadocs。如需将驱动程序 SDK 添加到您的应用,请执行以下操作:

  1. 将以下依赖项添加到您的 Gradle 或 Maven 配置中,注意要将 所需驱动程序 SDK 版本的 VERSION_NUMBER 占位符。

    Gradle

    将以下内容添加到 build.gradle 中:

    dependencies {
      ...
      implementation 'com.google.android.libraries.mapsplatform.transportation:transportation-driver:VERSION_NUMBER'
    }
    

    Maven

    将以下内容添加到 pom.xml 中:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.mapsplatform.transportation</groupId>
        <artifactId>transportation-driver</artifactId>
        <version>VERSION_NUMBER</version>
      </dependency>
    </dependencies>
    
  2. 驱动程序 SDK 依赖于 Navigation SDK,此依赖项在下面的代码中配置 如果需要特定版本的 Navigation SDK 在 build 配置文件中明确定义,如下所示: 省略上述代码块将使项目始终能够下载 主要发布版本中最新版本的 Navigation SDK。 请注意,最新版本的驱动程序 SDK 和 Navigation SDK 在发布之前经过了严格的测试。

    安排开发和版本的依赖项配置 环境

    Gradle

    将以下内容添加到 build.gradle 中:

    dependencies {
      ...
      implementation 'com.google.android.libraries.navigation:navigation:5.0.0'
    }
    

    Maven

    将以下内容添加到 pom.xml 中:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.navigation</groupId>
        <artifactId>navigation</artifactId>
        <version>5.0.0</version>
      </dependency>
    </dependencies>
    

向您的应用添加 API 密钥

将驱动程序 SDK 添加到您的应用后,请将 API 密钥添加到您的应用中。您 必须使用您在创建 API 时获取的项目 API 密钥, 设置您的开发项目

本部分介绍了如何存储 API 密钥,以提高其安全性 由您的应用引用您不应将 API 密钥签入您的版本 控制系统它应存储在 local.properties 文件中,即 位于项目的根目录下。如需详细了解 local.properties 文件,请参阅 Gradle 属性文件

为了简化此任务,您可以使用 Android 版 Secrets Gradle 插件

如需安装此插件并存储您的 API 密钥,请执行以下操作:

  1. 打开根级 build.gradle 文件,并将以下代码添加到 buildscript 下的 dependencies 元素。

    Groovy

    buildscript {
        dependencies {
            // ...
            classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0"
        }
    }
    

    Kotlin

    buildscript {
        dependencies {
            // ...
            classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0")
        }
    }
    
  2. 打开应用级 build.gradle 文件,并将以下代码添加到 plugins 元素。

    Groovy

    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
    

    Kotlin

    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    
  3. 如果您使用 Android Studio 将项目与 Gradle 同步

  4. 在项目级目录中打开 local.properties,然后添加 代码。将 YOUR_API_KEY 替换为您的 API 密钥。

    MAPS_API_KEY=YOUR_API_KEY
    
  5. AndroidManifest.xml 文件中,转到 com.google.android.geo.API_KEY 并按如下所示更新 android:value 属性:

    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="${MAPS_API_KEY}" />
    

以下示例展示了示例应用的完整清单:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.driverapidemo">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/_AppTheme">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

在您的应用中添加必要的提供方说明

如果您在应用中使用驱动程序 SDK,则必须添加 应用的法律声明中的署名文本和开源许可 部分。最好将提供方说明作为独立的菜单项或 (位于关于菜单项中)。

您可以在“third_party_licenses.txt”中找到许可信息。文件 已取消归档的 AAR 文件。

请参阅 https://developers.google.com/android/guides/opensource

依赖项

如果您使用 ProGuard 执行以下操作: 优化构建时,可能需要将以下几行代码添加到 ProGuard 配置文件:

-dontwarn com.google.**
-dontwarn okio.**

支持的最低 API 级别为 23。

初始化 SDK

必须提供提供方 ID(通常是 Google Cloud 项目 ID)才能 初始化 DriverContext 对象。如需详细了解如何设置 Google Cloud 项目,请参阅 身份验证和授权

在使用驱动程序 SDK 之前,您必须先初始化 Navigation SDK。要初始化 SDK,请执行以下操作:

  1. NavigationApi 获取 Navigator 对象。

    Java

    NavigationApi.getNavigator(
        this, // Activity
        new NavigationApi.NavigatorListener() {
          @Override
          public void onNavigatorReady(Navigator navigator) {
            // Keep a reference to the Navigator (used to configure and start nav)
            this.navigator = navigator;
          }
        }
    );
    

    Kotlin

    NavigationApi.getNavigator(
      this, // Activity
      object : NavigatorListener() {
        override fun onNavigatorReady(navigator: Navigator) {
          // Keep a reference to the Navigator (used to configure and start nav)
          this@myActivity.navigator = navigator
        }
      },
    )
    
  2. 创建一个用于填充必填字段的 DriverContext 对象。

    Java

    DriverContext driverContext = DriverContext.builder(application)
        .setProviderId(providerId)
        .setVehicleId(vehicleId)
        .setAuthTokenFactory(authTokenFactory)
        .setNavigator(navigator)
        .setRoadSnappedLocationProvider(
            NavigationApi.getRoadSnappedLocationProvider(application))
        .build();
    

    Kotlin

    val driverContext =
      DriverContext.builder(application)
        .setProviderId(providerId)
        .setVehicleId(vehicleId)
        .setAuthTokenFactory(authTokenFactory)
        .setNavigator(navigator)
        .setRoadSnappedLocationProvider(NavigationApi.getRoadSnappedLocationProvider(application))
        .build()
    
  3. 使用 DriverContext 对象初始化 *DriverApi

    Java

    RidesharingDriverApi ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext);
    

    Kotlin

    val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext)
    
  4. 从 API 对象获取 RidesharingVehicleReporter。 (*VehicleReporter 扩展了 NavigationVehicleReporter。)

    Java

    RidesharingVehicleReporter vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter();
    

    Kotlin

    val vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter()
    

使用 AuthTokenFactory 进行身份验证

当驱动程序 SDK 生成位置信息更新时, 它必须将这些更新 Fleet Engine 服务器为了对这些请求进行身份验证, 驱动程序 SDK 将调用调用方提供的 AuthTokenFactory 的实例。 工厂负责在以下位置生成身份验证令牌: 更新时间。

生成令牌的具体方式取决于每个开发者的具体情况。 不过,实现可能需要:

  • 从 HTTPS 服务器提取身份验证令牌(可能是 JSON 格式)
  • 解析并缓存令牌
  • 在令牌过期时刷新令牌

如需详细了解 Fleet Engine 服务器预期的令牌,请参阅 创建用于授权的 JSON 网络令牌 (JWT)

以下是 AuthTokenFactory 的框架实现:

Java

class JsonAuthTokenFactory implements AuthTokenFactory {
  private String token;  // initially null
  private long expiryTimeMs = 0;

  // This method is called on a thread whose only responsibility is to send
  // location updates. Blocking is OK, but just know that no location updates
  // can occur until this method returns.
  @Override
  public String getToken(AuthTokenContext authTokenContext) {
    if (System.currentTimeMillis() > expiryTimeMs) {
      // The token has expired, go get a new one.
      fetchNewToken(authTokenContext.getVehicleId());
    }
    return token;
  }

  private void fetchNewToken(String vehicleId) {
    String url =
        new Uri.Builder()
            .scheme("https")
            .authority("yourauthserver.example")
            .appendPath("token")
            .appendQueryParameter("vehicleId", vehicleId)
            .build()
            .toString();

    try (Reader r = new InputStreamReader(new URL(url).openStream())) {
      com.google.gson.JsonObject obj
          = com.google.gson.JsonParser.parseReader(r).getAsJsonObject();
      token = obj.get("Token").getAsString();
      expiryTimeMs = obj.get("TokenExpiryMs").getAsLong();

      // The expiry time could be an hour from now, but just to try and avoid
      // passing expired tokens, we subtract 10 minutes from that time.
      expiryTimeMs -= 10 * 60 * 1000;
    } catch (IOException e) {
      // It's OK to throw exceptions here. The StatusListener you passed to
      // create the DriverContext class will be notified and passed along the failed
      // update warning.
      throw new RuntimeException("Could not get auth token", e);
    }
  }
}

Kotlin

class JsonAuthTokenFactory : AuthTokenFactory() {

  private var token: String = ""
  private var expiryTimeMs: Long = 0

  // This method is called on a thread whose only responsibility is to send
  // location updates. Blocking is OK, but just know that no location updates
  // can occur until this method returns.
  override fun getToken(context: AuthTokenContext): String {
    if (System.currentTimeMillis() > expiryTimeMs) {
      // The token has expired, go get a new one.
      fetchNewToken(authTokenContext.getVehicleId())
    }
     return token
  }

  fun fetchNewToken(vehicleId: String) {
    val url =
      Uri.Builder()
        .scheme("https")
        .authority("yourauthserver.example")
        .appendPath("token")
        .appendQueryParameter("vehicleId", vehicleId)
        .build()
        .toString()

    try {
      val reader = InputStreamReader(URL(url).openStream())

      reader.use {
        val obj = com.google.gson.JsonParser.parseReader(r).getAsJsonObject()

        token = obj.get("ServiceToken").getAsString()
        expiryTimeMs = obj.get("TokenExpiryMs").getAsLong()

        // The expiry time could be an hour from now, but just to try and avoid
        // passing expired tokens, we subtract 10 minutes from that time.
        expiryTimeMs -= 10 * 60 * 1000
      }
    } catch (e: IOException) {
      // It's OK to throw exceptions here. The StatusListener you passed to
      // create the DriverContext class will be notified and passed along the failed
      // update warning.
      throw RuntimeException("Could not get auth token", e)
    }
  }
}

这一特定实现使用内置的 Java HTTP 客户端提取 从开发者身份验证服务器获取 JSON 格式的令牌。令牌为 以便重复使用如果旧令牌不超过 10 分钟,则重新获取令牌 过期时间。

您的实现可能会以不同的方式执行操作,例如使用后台线程 刷新令牌。

除非发生,否则 AuthTokenFactory 中的异常将被视为暂时异常 。多次尝试后,驱动程序 SDK 会假设 错误是永久性的,并且会停止尝试发送更新。

StatusListener 的状态和 Error Reporting

由于驱动程序 SDK 在 后台运行时,使用 StatusListener 在发生特定情况时触发通知 比如错误、警告或调试消息。错误可能是 它们是暂时性的(例如 BACKEND_CONNECTIVITY_ERROR),也可能是 会导致位置信息更新永久停止(如 VEHICLE_NOT_FOUND、 表示出现配置错误)。

您可以提供如下所示的可选 StatusListener 实现:

Java

class MyStatusListener implements StatusListener {
  /** Called when background status is updated, during actions such as location reporting. */
  @Override
  public void updateStatus(
      StatusLevel statusLevel, StatusCode statusCode, String statusMsg) {
    // Status handling stuff goes here.
    // StatusLevel may be DEBUG, INFO, WARNING, or ERROR.
    // StatusCode may be DEFAULT, UNKNOWN_ERROR, VEHICLE_NOT_FOUND,
    // BACKEND_CONNECTIVITY_ERROR, or PERMISSION_DENIED.
  }
}

Kotlin

class MyStatusListener : StatusListener() {
  /** Called when background status is updated, during actions such as location reporting. */
  override fun updateStatus(statusLevel: StatusLevel, statusCode: StatusCode, statusMsg: String) {
    // Status handling stuff goes here.
    // StatusLevel may be DEBUG, INFO, WARNING, or ERROR.
    // StatusCode may be DEFAULT, UNKNOWN_ERROR, VEHICLE_NOT_FOUND,
    // BACKEND_CONNECTIVITY_ERROR, or PERMISSION_DENIED.
  }
}

SSL/TLS 注意事项

在内部,驱动程序 SDK 实现使用 使用 SSL/TLS 安全地进行通信 与 Fleet Engine 服务器相关联。旧版 Android(API 版本 19 或 可能需要 SecurityProvider 补丁才能与 服务器。您应该会看到 文章 。这篇文章还 包含用于修补安全提供程序的代码示例。

启用位置信息更新

拥有 *VehicleReporter 实例后,启用位置信息更新 简单明了:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();

Kotlin

val reporter = ...

reporter.enableLocationTracking()

当车辆处于以下状态时,系统会定期发送位置信息更新 ONLINE。请注意,调用 reporter.enableLocationTracking() 不会 自动将车辆状态设置为 ONLINE。您必须 设置车辆状态

默认情况下,报告间隔为 10 秒。报告间隔可以 用 reporter.setLocationReportingInterval(long, TimeUnit) 更改。通过 支持的最小更新间隔为 5 秒。更新频率可能会更高 会导致请求速度变慢和错误

停用位置更新

换档完成后,系统可以停止位置信息更新, 已通过电话将车辆标记为离线 DeliveryVehicleReporter.disableLocationTrackingRidesharingVehicleReporter.disableLocationTracking

此调用将导致安排一个最终更新立即传送, 表示车辆处于离线状态。此更新不会包含 位置。

设置车辆状态

启用位置信息更新后,将车辆状态设置为 ONLINE 会 使车辆支持 SearchVehicles 查询;同样地,将 车辆设置为 OFFLINE 会将车辆标记为不可用。

您可以选择在服务器端设置车辆状态(请参阅更新 车辆), 或直接在驱动程序 SDK 中运行以下命令:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();
reporter.setVehicleState(VehicleState.ONLINE);

Kotlin

val reporter = ...

reporter.enableLocationTracking()
reporter.setVehicleState(VehicleState.ONLINE)

启用位置信息更新后,对 setVehicleState 的调用将在 下一次位置信息更新。

在未启用位置信息跟踪功能的情况下,将车辆标记为ONLINE会导致 在 IllegalStateException 中。在以下情况下,车辆可以标记为OFFLINE: 尚未启用或明确停用位置跟踪功能。这会导致 立即更新。对 RidesharingVehicleReporter.disableLocationTracking()将会 将车辆状态设为 OFFLINE

请注意,setVehicleState 会立即返回,并且会在 位置更新线程。类似于对位置更新的错误处理 更新车辆状态的操作是使用可选的 在 DriverContext 中设置的 StatusListener