開始使用 Android 版 Consumer SDK

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

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

基本系統需求

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

建構和依附元件設定

您可透過 Google Maven 存放區使用 Consumer SDK 1.99.0 以上版本。先前使用的私人存放區管道已淘汰。

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 控制台專案,或選取現有專案,以便搭配 Consumer 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 金鑰」一文。

在應用程式中加入 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 設定,將 VERSION_NUMBER 預留位置替換成所需的 Consumer SDK 版本。

    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 服務所需的服務權杖。這個 API 也會提供背景資訊所需的 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 使用手冊

應用程式應按照 Consumer 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 v2.x.x 支援 Maps SDK for Android 18.1.0 以上版本。下表摘要列出 Maps SDK 版本的預設轉譯器,以及這兩種轉譯器的支援性。建議您使用最新的轉譯器,但如果需要使用舊版轉譯器,則可使用 MapsInitializer.initialize() 明確指定。

Maps SDK 版本 支援最新版轉譯器 支援舊版轉譯器 預設轉譯器
V18.1.0 以下版本 舊版*
V18.2.0 最新

* 隨著新版地圖轉譯器推出,新版轉譯器將預設為最新版。

新增 Maps SDK 做為依附元件

Gradle

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

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

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() 並等待轉譯器要求結果,再初始化 Consumer 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 的應用程式版面配置 XML 檔案內)。接著,片段 (或檢視區塊) 會提供旅程共用地圖的存取權,供應用程式存取及修改。地圖也提供 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 for Android 內建解決方案:AutoCamera

為讓您專注於旅程而非裝置位置資訊,Consumer SDK 提供預設啟用的 AutoCamera 功能。鏡頭會放大,將焦點放在旅程分享路線和下一個行程路線控點。

AutoCamera

自訂相機行為

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

ConsumerController.getCameraUpdate() 目前會傳回目前建議的相機邊界。然後您便可以提供此 CameraUpdate 做為引數,給 GoogleMap.moveCamera()GoogleMap.animateCamera()

存取代僱駕駛服務與地圖

如要在應用程式中支援共乘和地圖互動功能,您需要存取 ConsumerGoogleMapConsumerControllerConsumerMapFragmentConsumerMapView 都會以非同步方式傳回 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 類別的包裝函式類別。該應用程式可讓您使用與 GoogleMap 相等的 API 與地圖互動。使用消費者地圖,您的應用程式和共乘服務就能與相同的基礎 Google 地圖完美互動。例如,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 必須是服務供應商擁有的雲端專案 ID。
401 未驗證 JWT 權杖無效,因此無法驗證要求。若簽署的 JWT 權杖沒有行程 ID,或是 JWT 權杖已過期,就會發生這個錯誤。
403 PERMISSION_DENIED 用戶端權限不足。如果 JWT 憑證無效、用戶端沒有權限,或用戶端專案未啟用 API,就會發生這個錯誤。JWT 權杖可能遺失,或權杖簽署的行程 ID 與要求的行程 ID 不符。
429 RESOURCE_EXHAUSTED 資源配額為 0 或流量頻率超出限制
503 無法提供 服務無法使用,伺服器通常會停止運作。
504 DEADLINE_EXCEEDED 已超出要求期限。只有在呼叫端設定的期限短於方法的預設期限 (例如,要求的期限不夠讓伺服器處理要求),且要求未在期限內完成時,才會發生上述情況。

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