您可以使用 Driver SDK 为“行程和订单进度”应用提供增强的导航和跟踪功能。Driver SDK 可为按需行程和配送解决方案舰队引擎提供车辆位置信息和任务更新。
Driver SDK 可让 Fleet Engine 服务和自定义服务了解车辆的位置和状态。例如,车辆可以是 ONLINE
或 OFFLINE
,并且车辆位置会随着行程的进行而变化。
最低系统要求
移动设备必须搭载 Android 5.0(API 级别 21)或更高版本。
Maven 配置
驱动程序 SDK 4.99 及更高版本可通过 Google Maven 制品库获取。
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>
项目配置
如需使用 Driver SDK,您的应用必须以 minSdkVersion
21 或更高版本为目标平台。
如需运行使用驱动程序 SDK 构建的应用,Android 设备必须安装 Google Play 服务。
设置您的开发项目
如需在 Google Cloud 控制台上设置开发项目并获取该项目的 API 密钥,请执行以下操作:
创建新的 Google Cloud 控制台项目,或选择现有项目,以便与驱动程序 SDK 搭配使用。等待几分钟,直到新项目显示在 Google Cloud 控制台中。
为了运行演示版应用,您的项目必须有权访问 Maps SDK for Android。在 Google Cloud 控制台中,选择 API 和服务 > 库,然后搜索并启用 Maps SDK for Android。
依次选择 API 和服务 > 凭据 > 创建凭据 > API 密钥,获取项目的 API 密钥。 如需详细了解如何获取 API 密钥,请参阅获取 API 密钥。
将 Driver SDK 添加到您的应用
Driver SDK 可通过专用 Maven 制品库获取。该代码库包含 SDK 的项目对象模型 (.pom) 文件和 Javadocs。如需将 Driver SDK 添加到您的应用,请执行以下操作:
将以下依赖项添加到您的 Gradle 或 Maven 配置中,并将
VERSION_NUMBER
占位符替换为所需的驱动程序 SDK 版本。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>
向您的应用添加 API 密钥
将 Driver SDK 添加到您的应用后,请将 API 密钥添加到您的应用。您必须使用设置开发项目时获得的项目 API 密钥。
本部分介绍了如何存储 API 密钥,以便您的应用可以更安全地引用该密钥。您不应将 API 密钥签入版本控制系统。该文件应存储在 local.properties
文件中,该文件位于项目的根目录中。如需详细了解 local.properties
文件,请参阅 Gradle 属性文件。
为了简化此任务,您可以使用 Android 版 Secrets Gradle 插件。
如需安装此插件并存储您的 API 密钥,请执行以下操作:
打开根级
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") } }
打开应用级
build.gradle
文件,并将以下代码添加到plugins
元素中。Groovy
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
Kotlin
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
如果您使用 Android Studio,请将项目与 Gradle 同步。
在项目级目录中打开
local.properties
,然后添加以下代码。将YOUR_API_KEY
替换为您的 API 密钥。MAPS_API_KEY=YOUR_API_KEY
在
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,则必须在应用的法律声明部分包含提供方说明文本和开源许可。最好以独立菜单项的形式提供提供方说明,或将提供方说明添加为关于菜单项的一部分。
您可以在驱动程序 SDK ZIP 文件中找到所需的提供方说明文本和开源许可:
NOTICE.txt
LICENSES.txt
依赖项
如果您使用 ProGuard 优化 build,则可能需要将以下行添加到 ProGuard 配置文件:
-dontwarn com.google.**
-dontwarn okio.**
支持的最低 API 级别为 21。
初始化 SDK
如需初始化 DriverContext
对象,必须提供提供方 ID(通常是 Google Cloud 项目 ID)。如需详细了解如何设置 Google Cloud 项目,请参阅身份验证和授权。
在使用 Driver SDK 之前,您必须先初始化 Navigation SDK。要初始化 SDK,请执行以下操作:
从
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 } }, )
创建一个
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()
使用
DriverContext
对象初始化*DriverApi
。Java
RidesharingDriverApi ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext);
Kotlin
val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext)
从 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 Web 令牌 (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
由于 Driver 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
补丁程序才能与服务器通信。如需详细了解如何在 Android 中使用 SSL,请查看这篇文章。该文章还包含用于修补安全提供程序的代码示例。
启用位置信息更新
有了 *VehicleReporter
实例后,就可以直接启用位置信息更新:
Java
RidesharingVehicleReporter reporter = ...;
reporter.enableLocationTracking();
Kotlin
val reporter = ...
reporter.enableLocationTracking()
当车辆状态为 ONLINE
时,系统会定期发送位置信息更新。请注意,调用 reporter.enableLocationTracking()
不会自动将车辆状态设置为 ONLINE
。您必须明确设置车辆状态。
默认情况下,报告间隔为 10 秒。可通过 reporter.setLocationReportingInterval(long, TimeUnit)
更改报告间隔。支持的最小更新间隔为 5 秒。更频繁的更新可能会导致请求速度变慢,并导致错误。
停用位置信息更新
驾驶员完成轮班后,可通过调用 DeliveryVehicleReporter.disableLocationTracking
或 RidesharingVehicleReporter.disableLocationTracking
停止位置信息更新并将车辆标记为离线。
此调用将安排最后一次更新立即交付,表明车辆处于离线状态。此更新不会包含用户的位置信息。
设置车辆状态
启用位置信息更新后,将车辆状态设置为 ONLINE
即可让车辆可用于 SearchVehicles
查询;同样,将车辆标记为 OFFLINE
也会将车辆标记为不可用。
您可以选择在服务器端设置车辆状态(请参阅更新车辆),也可以直接在 Driver 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
集来传播。