開始使用 Android 版 Consumer SDK

您可以使用 Consumer SDK 建構及執行與隨選乘車和運送服務解決方案後端服務整合的基本消費者應用程式。您可以建立「行程」和「訂單進度」應用程式,顯示目前的行程、回應行程更新及處理行程錯誤。

由於 Consumer SDK 採用模組化架構,因此您可以使用要用於特定應用程式的 API 部分,並將這些部分與您自己的 API、Fleet Engine 提供的後端服務整合,以及 Google 地圖平台的新增 API。

基本系統需求

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

建構與依附元件設定

消費者 SDK 1.99.0 以上版本可以透過 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>

專案設定

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

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

設定開發專案

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

  1. 建立新的 Google Cloud Console 專案,或選取現有專案,以便與消費者 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 金鑰」一文。

在應用程式中新增 Consumer SDK

Consumer SDK 可透過私人 Maven 存放區取得。這個存放區包括 SDK 的專案物件模型 (.pom) 檔案和 Javadocs。如何在應用程式中加入 Consumer SDK:

  1. 如上一節所述,設定您的環境以存取主機 Maven 存放區。

    如果您已在 settings.gradle 中宣告集中式依附元件管理設定,請按照下列步驟停用該設定。

    • 移除 settings.gradle 中的下列程式碼區塊:

      import org.gradle.api.initialization.resolve.RepositoriesMode
      dependencyResolutionManagement {
          repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
          repositories {
              google()
              mavenCentral()
          }
      }
      
  2. 將以下依附元件新增至 Gradle 或 Maven 設定,以所需 Consumer SDK 版本的 VERSION_NUMBER 預留位置取代。

    Gradle

    請將以下內容新增到 build.gradle 中:

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

    Maven

    請將以下內容新增到 pom.xml 中:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.mapsplatform.transportation</groupId>
        <artifactId>transportation-consumer</artifactId>
        <version>VERSION_NUMBER</version>
      </dependency>
    </dependencies>
    
  3. Consumer SDK 須依附於 Maps SDK。這個依附元件的設定方式如下:如果 Maps SDK 版本「並未」在建構設定檔中明確定義 (如下所示),當新版本的 Maps SDK 推出時,Consumer SDK 將繼續採用系統要求的最低支援 Maps SDK 版本。

    Gradle

    請將以下內容新增到 build.gradle 中:

    dependencies {
      ...
      implementation 'com.google.android.gms:play-services-maps:18.1.0'
    }
    

    Maven

    請將以下內容新增到 pom.xml 中:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.gms</groupId>
        <artifactId>play-services-maps</artifactId>
        <version>18.1.0</version>
      </dependency>
    </dependencies>
    

在應用程式中加入 API 金鑰

將 Consumer 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.consumerapidemo">
    <uses-permission android:name="android.permission.ACCESS_FINE_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>

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

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

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

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

消費者 SDK 驗證

Consumer SDK 會使用 JSON Web Token 提供驗證。JSON Web Token (JWT) 是一種 JSON 基礎存取權杖,可針對服務提供一或多個憑證附加資訊。例如,伺服器可能會產生憑證附加資訊「以管理員的身分登入」,然後將該憑證提供給用戶端。然後,用戶端就可以使用該憑證證明自己是以管理員的身分登入。

Consumer SDK 會使用應用程式提供的 JSON Web Token 與 Fleet Engine 通訊。如需詳細資訊,請參閱「Fleet Engine 驗證與授權」。

授權權杖必須在權杖的 authorization 標頭中加入 tripid:TRIP_ID 憑證附加資訊,其中 TRIP_ID 是行程 ID。這樣消費者 SDK 就能存取行程詳細資料,包括車輛位置、路線和預計到達時間。

JSON Web Token 回呼

Consumer SDK 會在初始化期間,向應用程式註冊授權權杖回呼。SDK 會呼叫應用程式,針對所有需要授權的網路要求取得權杖。

強烈建議您設定回呼實作的快取授權權杖,並只在 expiry 時間過後重新整理。憑證應有一小時的有效期限。

授權權杖回呼會指定 TripService 服務所需的服務權杖。並提供結構定義所需的 tripId

以下程式碼範例示範如何實作授權權杖回呼。

Java

class JsonAuthTokenFactory implements AuthTokenFactory {

  private static final String TOKEN_URL =
      "https://yourauthserver.example/token";

  private static class CachedToken {
    String tokenValue;
    long expiryTimeMs;
    String tripId;
  }

  private CachedToken token;

  /*
  * This method is called on a background thread. Blocking is OK. However, be
  * aware that no information can be obtained from Fleet Engine until this
  * method returns.
  */
  @Override
  public String getToken(AuthTokenContext context) {
    // If there is no existing token or token has expired, go get a new one.
    String tripId = context.getTripId();
    if (tripId == null) {
      throw new RuntimeException("Trip ID is missing from AuthTokenContext");
    }
    if (token == null || System.currentTimeMillis() > token.expiryTimeMs ||
        !tripId.equals(token.tripId)) {
      token = fetchNewToken(tripId);
    }
    return token.tokenValue;
  }

  private static CachedToken fetchNewToken(String tripId) {
    String url = TOKEN_URL + "/" + tripId;
    CachedToken token = new CachedToken();

    try (Reader r = new InputStreamReader(new URL(url).openStream())) {
      com.google.gson.JsonObject obj
          = com.google.gson.JsonParser.parseReader(r).getAsJsonObject();

      token.tokenValue = obj.get("ServiceToken").getAsString();
      token.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 5 minutes from that time.
      */
      token.expiryTimeMs -= 5 * 60 * 1000;
    } catch (IOException e) {
      /*
      * It's OK to throw exceptions here. The error listeners will receive the
      * error thrown here.
      */
      throw new RuntimeException("Could not get auth token", e);
    }
    token.tripId = tripId;

    return token;
  }
}

Kotlin

class JsonAuthTokenFactory : AuthTokenFactory() {

  private var token: CachedToken? = null

  /*
  * This method is called on a background thread. Blocking is OK. However, be
  * aware that no information can be obtained from Fleet Engine until this
  * method returns.
  */
  override fun getToken(context: AuthTokenContext): String {
    // If there is no existing token or token has expired, go get a new one.
    val tripId = 
      context.getTripId() ?: 
        throw RuntimeException("Trip ID is missing from AuthTokenContext")

    if (token == null || System.currentTimeMillis() > token.expiryTimeMs ||
        tripId != token.tripId) {
      token = fetchNewToken(tripId)
    }

    return token.tokenValue
  }

  class CachedToken(
    var tokenValue: String? = "", 
    var expiryTimeMs: Long = 0,
    var tripId: String? = "",
  )

  private companion object {
    const val TOKEN_URL = "https://yourauthserver.example/token"

    fun fetchNewToken(tripId: String) {
      val url = "$TOKEN_URL/$tripId"
      val token = CachedToken()

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

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

          token.tokenValue = obj.get("ServiceToken").getAsString()
          token.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 5 minutes from that time.
          */
          token.expiryTimeMs -= 5 * 60 * 1000
        }
      } catch (e: IOException) {
        /*
        * It's OK to throw exceptions here. The error listeners will receive the
        * error thrown here.
        */
        throw RuntimeException("Could not get auth token", e)
      }

      token.tripId = tripId

      return token
    }
  }
}

初始化 API

在按照這些程序之前,假設您已啟用適當的服務和 Consumer SDK。

取得 ConsumerApi 執行個體

如要使用 Consumer SDK,應用程式必須以非同步方式初始化 ConsumerApi。API 是單例模式。初始化方法會採用 AuthTokenFactory。工廠會在必要時為使用者產生新的 JWT 憑證。

providerId 是 Google Cloud 專案的專案 ID。如要進一步瞭解如何建立專案,請參閱 Fleet Engine 使用手冊

應用程式應按照消費者 SDK 驗證的說明實作 AuthTokenFactory

Java

Task<ConsumerApi> consumerApiTask = ConsumerApi.initialize(
    this, "myProviderId", authTokenFactory);

consumerApiTask.addOnSuccessListener(
  consumerApi -> this.consumerApi = consumerApi);

Kotlin

val consumerApiTask =
  ConsumerApi.initialize(this, "myProviderId", authTokenFactory)

consumerApiTask?.addOnSuccessListener { consumerApi: ConsumerApi ->
  this@YourActivity.consumerApi = consumerApi
}

初始化 Maps SDK 以要求偏好的轉譯器

Consumer SDK 2.0.0 版支援 Maps SDK for Android 18.1.0 以上版本。支援指定偏好的 Google 地圖轉譯器的要求。詳情請參閱「新版地圖轉譯器(選用)」一文。

將 Maps SDK 新增為依附元件

Gradle

請將以下內容新增到 build.gradle 中:

dependencies {
  //...
  implementation "com.google.android.gms:play-services-maps:18.1.0"
}

Maven

請將以下內容新增到 pom.xml 中:

 <dependencies>
   ...
   <dependency>
     <groupId>com.google.android.gms</groupId>
     <artifactId>play-services-maps</artifactId>
     <version>18.1.0</version>
   </dependency>
 </dependencies>

在初始化 Consumer SDK 之前初始化 Maps SDK

Application 或啟動 Activity 類別中,呼叫 MapsInitializer.initialize(),等待轉譯器要求結果後,再初始化取用端 SDK。

java

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  initViews();

  MapsInitializer.initialize(getApplicationContext(), Renderer.LATEST,
      new OnMapsSdkInitializedCallback() {
        @Override
        public void onMapsSdkInitialized(Renderer renderer) {
          switch (renderer) {
            case LATEST:
              Log.i("maps_renderer", "LATEST renderer");
              break;
            case LEGACY:
              Log.i("maps_renderer", "LEGACY renderer");
              break;
          }

          initializeConsumerSdk();
        }
      });
}

Kotlin

fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.main)
  initViews()

  MapsInitializer.initialize(
    getApplicationContext(), Renderer.LATEST,
    object : OnMapsSdkInitializedCallback() {
      fun onMapsSdkInitialized(renderer: Renderer?) {
        when (renderer) {
          LATEST -> Log.i("maps_renderer", "LATEST renderer")
          LEGACY -> Log.i("maps_renderer", "LEGACY renderer")
        }
        initializeConsumerSdk()
      }
    })
  }

建立使用者介面

您可以使用 ConsumerMapFragmentConsumerMapView 為應用程式建立使用者介面。ConsumerMapFragment 可讓您使用 Fragment 定義地圖,ConsumerMapView 則可讓您使用 ViewConsumerMapViewConsumerMapFragment 都具備相同的共乘功能,您可以根據應用程式適合使用 ViewFragment 來選擇。

新增對 API 19 (KitKat) 和向量可繪項目的支援

如果您的應用程式設計需要支援 API 19 (KitKat) 裝置和向量可繪項目,請在活動中加入下列程式碼。這個程式碼會擴充 AppCompatActivity,以使用 Consumer SDK 中的向量可繪項目。

Java

// ...
import android.support.v7.app.AppCompatActivity;

// ...

public class ConsumerTestActivity extends AppCompatActivity {
  // ...
}

Kotlin

// ...
import android.support.v7.app.AppCompatActivity

// ...

class ConsumerTestActivity : AppCompatActivity() {
  // ...
}

新增地圖片段或檢視畫面

您需要在 Android 片段或檢視區塊 (位於 /res/layout 中) 中定義的 Android 片段或檢視區塊中,建立用來顯示旅程分享的地圖。接著,片段 (或檢視區塊) 可讓您存取旅程分享地圖,供應用程式存取及修改。地圖也提供 ConsumerController 的控制代碼,可讓應用程式控制及自訂旅程分享體驗。

旅程分享地圖和控制器

您可以將旅程共用地圖定義為片段 (使用 ConsumerMapFragment) 或檢視畫面 (使用 ConsumerMapView),如以下程式碼範例所示。接著,onCreate() 方法應呼叫 getConsumerGoogleMapAsync(callback),進而在回呼中以非同步方式傳回 ConsumerGoogleMap。接著,您可以使用 ConsumerGoogleMap 顯示旅程分享資訊,且可視需要更新。

ConsumerMapFragment

您可以在應用程式版面配置 XML 檔案中定義片段,如以下程式碼範例所示。

<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.google.android.libraries.mapsplatform.transportation.consumer.view.ConsumerMapFragment"
    android:id="@+id/consumer_map_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

getConsumerGoogleMapAsync() 的呼叫應來自 onCreate() 方法。

Java

public class SampleAppActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {

    // Find the ConsumerMapFragment.
    ConsumerMapFragment consumerMapFragment =
        (ConsumerMapFragment) fragmentManager.findFragmentById(R.id.consumer_map_fragment);

    // Initiate the callback that returns the map.
    if (consumerMapFragment != null) {
      consumerMapFragment.getConsumerGoogleMapAsync(
          new ConsumerMapReadyCallback() {
            // The map returned in the callback is used to access the ConsumerController.
            @Override
            public void onConsumerMapReady(@NonNull ConsumerGoogleMap consumerGoogleMap) {
              ConsumerController consumerController = consumerGoogleMap.getConsumerController();
            }
          });
    }
  }

}

Kotlin

class SampleAppActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    // Find the ConsumerMapFragment.
    val consumerMapFragment =
      fragmentManager.findFragmentById(R.id.consumer_map_fragment) as ConsumerMapFragment

    consumerMapFragment.getConsumerGoogleMapAsync(
      object : ConsumerMapReadyCallback() {
        override fun onConsumerMapReady(consumerGoogleMap: ConsumerGoogleMap) {
          val consumerController = consumerGoogleMap.getConsumerController()!!
        }
      }
    )
  }
}
ConsumerMapView

檢視畫面可在片段或活動中使用,如 XML 檔案所定義。

<com.google.android.libraries.mapsplatform.transportation.consumer.view.ConsumerMapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/consumer_map_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

getConsumerGoogleMapAsync() 的呼叫應該來自 onCreate()。除了回呼參數之外,它還需要包含活動或片段以及 GoogleMapOptions (可以是空值),其中包含 MapView 的設定屬性。活動或片段基礎類別必須是 FragmentActivity 或支援 Fragment (分別提供),因為它們可提供生命週期的存取權。

Java

public class SampleAppActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    ConsumerMapView mapView = findViewById(R.id.consumer_map_view);

    if (mapView != null) {
      mapView.getConsumerGoogleMapAsync(
          new ConsumerMapReadyCallback() {
            // The map returned in the callback is used to access the ConsumerController.
            @Override
            public void onConsumerMapReady(@NonNull ConsumerGoogleMap consumerGoogleMap) {
              ConsumerController consumerController = consumerGoogleMap.getConsumerController();
            }
          }, this, null);
    }
  }

}

Kotlin

class SampleAppActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    val mapView = findViewById(R.id.consumer_map_view) as ConsumerMapView

    mapView.getConsumerGoogleMapAsync(
      object : ConsumerMapReadyCallback() {
        // The map returned in the callback is used to access the ConsumerController.
        override fun onConsumerMapReady(consumerGoogleMap: ConsumerGoogleMap) {
          val consumerController = consumerGoogleMap.getConsumerController()!!
        }
      },
      /* fragmentActivity= */ this,
      /* googleMapOptions= */ null,
    )
  }
}

片段中的 MapView 與活動中的 MapView 範例相同,差別在於片段會加載 onCreateView() 方法中包含 MapView 的版面配置。

Java

public class MapViewInFragment extends Fragment {

  @Override
  public View onCreateView(
      @NonNull LayoutInflater layoutInflater,
      @Nullable ViewGroup viewGroup,
      @Nullable Bundle bundle) {
    return layoutInflater.inflate(R.layout.consumer_map_view, viewGroup, false);
  }

}

Kotlin

class MapViewInFragment : Fragment() {
  override fun onCreateView(
    layoutInflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
  ): View {
    return layoutInflater.inflate(R.layout.consumer_map_view, viewGroup, false)
  }
}

調整相機鏡頭,將焦點放在某趟旅程中

Maps SDK 內建的預設「我的位置」按鈕,會將相機置於裝置位置中央。

如果有正在執行的旅程分享工作階段,建議您將攝影機聚焦在歷程的中心,而非裝置的位置。

Android 的 Consumer SDK 內建解決方案:AutoCamera

為了讓您專心處理歷程,而非裝置位置資訊,Consumer SDK 提供預設啟用的 AutoCamera 功能。攝影機會縮放畫面,聚焦在旅程分享路線和下一個行程路線點。

AutoCamera

自訂攝影機行為

如果您需要進一步控制相機行為,可以使用 ConsumerController.setAutoCameraEnabled() 停用或啟用自動相機。

ConsumerController.getCameraUpdate() 會傳回目前建議的鏡頭邊界。接著,您就可以提供這個 CameraUpdate 做為 GoogleMap.moveCamera()GoogleMap.animateCamera() 的引數。

使用代僱駕駛服務與地圖

如要在應用程式中支援共乘和地圖互動功能,您必須擁有 ConsumerGoogleMapConsumerController 的存取權。ConsumerMapFragmentConsumerMapView 都會以非同步方式傳回 ConsumerMapReadyCallback 中的 ConsumerGoogleMapConsumerGoogleMap 會從 getConsumerController() 傳回 ConsumerController。您可以透過下列方式存取 ConsumerGoogleMapConsumerController

Java

private ConsumerGoogleMap consumerGoogleMap;
private ConsumerController consumerController;
private ConsumerMapView consumerMapView;

consumerMapView.getConsumerGoogleMapAsync(
    new ConsumerMapReadyCallback() {
      @Override
      public void onConsumerMapReady(@NonNull ConsumerGoogleMap consumerMap) {
        consumerGoogleMap = consumerMap;
        consumerController = consumerMap.getConsumerController();
      }
    },
    this, null);

Kotlin

var consumerGoogleMap: ConsumerGoogleMap
var consumerController: ConsumerController
val consumerMapView = findViewById(R.id.consumer_map_view) as ConsumerMapView

consumerMapView.getConsumerGoogleMapAsync(
  object : ConsumerMapReadyCallback() {
    override fun onConsumerMapReady(consumerMap: ConsumerGoogleMap) {
      consumerGoogleMap = consumerMap
      consumerController = consumerMap.getConsumerController()
    },
    /* fragmentActivity= */ this,
    /* googleMapOptions= */ null,
  }
)

ConsumerGoogleMap

ConsumerGoogleMapGoogleMap 類別的包裝函式類別。這種 API 可讓應用程式使用相當於 GoogleMap 的 API 與地圖互動。使用消費者地圖,您的應用程式和共乘服務就能與相同的 GoogleMap 流暢互動。舉例來說,GoogleMap 僅允許註冊單一回呼,但 ConsumerGoogleMap 支援雙重註冊的回呼。這些回呼可讓應用程式和共乘服務註冊系統依序呼叫的回呼。

ConsumerController

ConsumerController 可讓您使用乘車分享功能,例如監控行程、控制行程狀態及設定地點。

設定旅程資料

在後端將消費者與車輛進行比對後,請使用 JourneySharingSession 啟動歷程分享使用者介面。分享行程資訊會顯示相符的車輛位置和路線在應用程式中導入 SDK 後,您可以新增用來監控行程、監聽更新及處理錯誤的功能。下列程序假設後端服務已經準備就緒,且用於比對消費者與車輛的服務可以正常運作。

  1. TripModel 物件上註冊事件監聽器,即可取得行程的詳細資料,例如預計到達時間 (預計抵達時間),以及車輛抵達前所需的移動距離。

    Java

    // Create a TripModel instance for listening to updates to the trip specified by this trip name.
    String tripName = ...;
    TripModelManager tripModelManager = consumerApi.getTripModelManager();
    TripModel tripModel = tripModelManager.getTripModel(tripName);
    
    // Create a JourneySharingSession instance based on the TripModel.
    JourneySharingSession session = JourneySharingSession.createInstance(tripModel);
    
    // Add the JourneySharingSession instance on the map for updating the UI.
    consumerController.showSession(session);
    
    // Register for trip update events.
    tripModel.registerTripCallback(new TripModelCallback() {
      @Override
      public void onTripETAToNextWaypointUpdated(
          TripInfo tripInfo, @Nullable Long timestampMillis) {
        // ...
      }
    
      @Override
      public void onTripActiveRouteRemainingDistanceUpdated(
          TripInfo tripInfo, @Nullable Integer distanceMeters) {
        // ...
      }
    
      // ...
    });
    

    Kotlin

    // Create a TripModel instance for listening to updates to the trip specified by this trip name.
    val tripName = "tripName"
    val tripModelManager = consumerApi.getTripModelManager()
    val tripModel = tripModelManager.getTripModel(tripName)
    
    // Create a JourneySharingSession instance based on the TripModel.
    val session = JourneySharingSession.createInstance(tripModel)
    
    // Add the JourneySharingSession instance on the map for updating the UI.
    consumerController.showSession(session)
    
    // Register for trip update events.
    tripModel.registerTripCallback(
      object : TripModelCallback() {
        override fun onTripETAToNextWaypointUpdated(
          tripInfo: TripInfo,
          timestampMillis: Long?,
        ) {
          // ...
        }
    
        override fun onTripActiveRouteRemainingDistanceUpdated(
          tripInfo: TripInfo,
          distanceMeters: Int?,
        ) {
          // ...
        }
    
      // ...
    })
    
  2. 使用 TripModelOptions 設定行程。

    Java

    // Set refresh interval to 2 seconds.
    TripModelOptions tripOptions =
        TripModelOptions.builder().setRefreshIntervalMillis(2000).build();
    tripModel.setTripModelOptions(tripOptions);
    

    Kotlin

    // Set refresh interval to 2 seconds.
    val tripOptions = TripModelOptions.builder().setRefreshIntervalMillis(2000).build()
    tripModel.setTripModelOptions(tripOptions)
    

停止分享旅程

請務必在不再需要分享歷程時停止分享歷程,例如在代管活動遭到刪除時。停止共用歷程也會停止對 Fleet Engine 的網路要求,並防止記憶體流失。

以下程式碼範例示範如何停止分享歷程。

Java

public class MainActivity extends AppCompatActivity
    implements ConsumerViewModel.JourneySharingListener  {

  // Class implementation

  @Override
  protected void onDestroy() {
    super.onDestroy();

    if (journeySharingSession != null) {
      journeySharingSession.stop();
    }
  }
}

Kotlin

class SampleAppActivity : AppCompatActivity(), ConsumerViewModel.JourneySharingListener {

  // Class implementation

  override fun onDestroy() {
    super.onDestroy()

    journeySharingSession?.stop()
  }
}

處理行程錯誤

onTripRefreshError 方法會顯示行程監控期間發生的錯誤。Consumer SDK 錯誤的對應也適用於 Google Cloud Platform 所製定的 HTTP/RPC 規定。行程監控期間常見的錯誤如下:

HTTP RPC 說明
400 INVALID_ARGUMENT 用戶端指定的行程名稱無效。行程名稱必須採用 providers/{provider_id}/trips/{trip_id} 格式。provider_id 必須是服務供應商擁有的 Cloud 專案 ID。
401 未驗證 JWT 憑證無效,因此未通過驗證。如果在沒有行程 ID 的情況下簽署 JWT 憑證,或是 JWT 憑證已過期,就會發生這個錯誤。
403 PERMISSION_DENIED 用戶端權限不足。如果 JWT 憑證無效、用戶端沒有權限,或用戶端專案未啟用 API,就會發生這個錯誤。JWT 權杖可能遺漏,或權杖簽署的行程 ID 與要求的行程 ID 不符。
429 RESOURCE_EXHAUSTED 資源配額為 0,或流量速率超過限制。
503 UNAVAILABLE 服務無法使用。通常是伺服器停止運作。
504 DEADLINE_EXCEEDED 已超出要求期限。只有在呼叫端設定的期限少於該方法的預設期限 (亦即,要求的期限不夠讓伺服器處理要求),且要求未在期限內完成時,才會發生。

詳情請參閱「消費者 SDK 錯誤處理」。