開始使用 Android 版 Driver SDK

您可以使用 Driver SDK 為行程和訂單進度應用程式提供強化的導航和追蹤功能。Driver SDK 會針對隨選乘車和配送解決方案 Fleet Engine 提供車輛位置和工作更新。

驅動程式 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>

專案設定

如要使用 Driver SDK,應用程式必須指定 minSdkVersion 23 以上版本。

如要執行以 Driver SDK 建構的應用程式,Android 裝置必須安裝 Google Play 服務

設定開發專案

如要在 Google Cloud 控制台上設定開發專案並取得專案的 API 金鑰,請按照下列指示操作:

  1. 建立新的 Google Cloud Console 專案,或選取現有專案,以便與 Driver SDK 搭配使用。稍等幾分鐘,等待新專案顯示在 Google Cloud 控制台中。

  2. 專案必須具備 Maps SDK for Android 的存取權,才能執行試用版應用程式。在 Google Cloud 控制台中,依序選取「APIs & Services」(API 和服務) >「Library」(程式庫),然後搜尋並啟用 Maps SDK for Android。

  3. 依序選取「APIs & Services」(API 和服務) >「Credentials」(憑證) >「Create credentials」(建立憑證) >「API key」(API 金鑰),取得專案的 API 金鑰。如要進一步瞭解如何取得 API 金鑰,請參閱「取得 API 金鑰」一文。

將驅動程式 SDK 新增至應用程式

您可以從 Google Maven 存放區取得驅動程式 SDK。這個存放區包括 SDK 的專案物件模型 (.pom) 檔案和 Javadocs。如何在應用程式中新增 Driver 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 時明確定義,如下所示。如省略上述的程式碼區塊,專案將一律下載主要發布版本的 Navigation SDK。請注意,最新版 Driver 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 金鑰

將 Driver SDK 新增至應用程式後,請將 API 金鑰新增至應用程式。您必須使用在設定開發專案時取得的專案 API 金鑰。

本節將說明如何儲存 API 金鑰,讓應用程式以更安全的方式參照金鑰。建議不要將 API 金鑰登錄到版本管控系統中。這個檔案應該儲存在位於專案根目錄的 local.properties 檔案中。如要進一步瞭解 local.properties 檔案,請參閱這篇文章中關於 Gradle 屬性檔案的說明

您可以使用 Secrets Gradle Plugin for Android 來簡化這項工作。

如要安裝這個外掛程式並儲存 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>

在應用程式中加入必要的作者資訊

如果您在應用程式中使用 Driver SDK,則必須在應用程式的法律聲明部分中加入作者資訊文字和開放原始碼授權。建議您將作者資訊以獨立選單項目的形式加入,或納入「About」選單項目中。

您可以在未封存的 AAR 檔案中的「third_party_licenses.txt」檔案找到授權資訊。

請參閱 https://developers.google.com/android/guides/opensource,瞭解如何加入開放原始碼通知。

依附元件

如果您使用 ProGuard 最佳化版本,可能需要在 ProGuard 設定檔中加入下列幾行內容:

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

支援的最低 API 級別為 23。

初始化 SDK

如要初始化 DriverContext 物件,必須提供提供者 ID (通常為 Google Cloud 專案 ID)。如要進一步瞭解如何設定 Google Cloud 專案,請參閱驗證與授權一文。

使用 Driver 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 伺服器。為驗證這些要求,Driver SDK 會呼叫呼叫端提供的 AuthTokenFactory 執行個體。工廠需負責在位置更新時產生驗證憑證。

至於系統如何根據各開發人員的情況產生權杖,不過,實作可能需要:

  • 從 HTTPS 伺服器擷取驗證權杖 (可能採用 JSON 格式)
  • 剖析並快取權杖
  • 在權杖過期時更新權杖

如要進一步瞭解 Fleet Engine 伺服器預期的權杖,請參閱建立 JSON Web Token (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 修補程式才能與伺服器通訊。如要進一步瞭解如何在 Android 中使用 SSL,請參閱這篇文章。以及用於修補安全性提供者的程式碼範例。

啟用位置更新功能

建立 *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 (選擇性) 設定,傳播更新車輛狀態的錯誤。