開始使用 Android 版 Driver SDK

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

驅動程式 SDK 可讓 Fleet Engine 服務和您的自訂服務掌握車輛的位置和狀態。舉例來說,車輛可以是 ONLINEOFFLINE,且車輛位置會隨著行程進度而改變。

基本系統需求

行動裝置必須搭載 Android 6.0 (API 級別 23) 以上版本。

建構和依附元件設定

您可以從 Google Maven 存放區取得 4.99 以上版本的驅動程式 SDK。

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 服務

設定開發專案

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

  1. 建立新的 Google Cloud 控制台專案,或選取現有專案,以便與驅動程式 SDK 搭配使用。請稍待幾分鐘,直到新專案顯示在 Google Cloud 控制台上。

  2. 如要執行試用版應用程式,專案必須具備 Maps SDK for Android 的存取權。在 Google Cloud 控制台中,選取「API 和服務」>「程式庫」,然後搜尋並啟用 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 設定,將 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>
    
  2. 驅動程式 SDK 依附 Navigation SDK,因此這個依附元件的設定方式可在需要特定版本的 Navigation SDK 時明確定義 (如下所示),省略上述程式碼區塊後,專案就會一律下載主要發布版本中最新版 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 金鑰

在應用程式中加入 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

需要提供者 ID (通常為 Google Cloud 專案 ID) 才能初始化 DriverContext 物件。如要進一步瞭解如何設定 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 伺服器。為驗證這些要求,驅動程式 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 來傳播。