遷移至新的 Places SDK 用戶端

本指南說明地點介面集相容性程式庫和新版 Places SDK for Android 獨立版本之間的異動。如果您正在使用地點介面集相容性程式庫,而不是遷移至新版 Places SDK for Android 的獨立版本,本指南將說明如何更新專案,改為使用新版 Places SDK for Android。

如要存取 Places SDK for Android 2.6.0 以上版本的功能和錯誤修正項目,只能使用 Places SDK for Android。Google 建議您盡快從相容性程式庫更新至新的 Places SDK for Android 版本。

異動內容

主要變動內容如下:

  • 新版的 Places SDK for Android 是以靜態用戶端程式庫的形式發布。在 2019 年 1 月之前,您可以透過 Google Play 服務提供 Places SDK for Android。此後提供地點介面集相容性程式庫,方便使用者轉換至新的 Places SDK for Android。
  • 我們提供全新方法
  • 欄位遮罩現在支援傳回地點詳細資料的方法。您可以使用欄位遮罩指定要傳回的地點資料類型,
  • 我們改善了用於回報錯誤的狀態碼
  • 自動完成功能現已支援工作階段符記
  • 地點挑選器已無法使用

關於地點介面集相容性程式庫

2019 年 1 月,獨立式 Places SDK for Android 1.0 版發布時,Google 提供了相容性程式庫,協助您從已停用的 Places SDK for Android 版本 (com.google.android.gms:play-services-places) 遷移至 Google Play 服務版本。

我們暫時提供這個相容性程式庫,以便將目標 Google Play 服務版本的 API 呼叫重新導向並轉譯為新的獨立版本,直到開發人員能遷移程式碼,在獨立 SDK 中使用新名稱。針對從 1.0 版到 2.6.0 版發布的每個 Places SDK for Android 版本,我們發布了對應的地點介面集相容性程式庫版本,以提供對等的功能。

凍結及淘汰地點介面集相容性程式庫

Places SDK for Android 所有版本的相容性程式庫已於 2022 年 3 月 31 日淘汰。2.6.0 版是地點介面集相容性程式庫的最新版本。如要存取 Places SDK for Android 2.6.0 以上版本的功能和錯誤修正項目,只能使用 Places SDK for Android。

如要使用 2.6.0 以上版本適用的新功能和重大錯誤修正,Google 建議您遷移至 Places SDK for Android。如果您目前使用相容性程式庫,請按照「安裝 Places SDK for Android」一節中的步驟操作,遷移至 Places SDK for Android。

安裝用戶端程式庫

新版 Places SDK for Android 是以靜態用戶端程式庫的形式發布。

使用 Maven 將 Places SDK for Android 加進 Android Studio 專案:

  1. 如果目前使用地點介面集相容性程式庫

    1. dependencies 區段中替換下列程式碼:

          implementation 'com.google.android.libraries.places:places-compat:X.Y.Z'

      透過此行程式碼切換至 Places SDK for Android:

          implementation 'com.google.android.libraries.places:places:3.3.0'

  2. 如果目前使用 Places SDK for Android 的 Play 服務版本

    1. dependencies 區段中替換下列程式碼:

          implementation 'com.google.android.gms:play-services-places:X.Y.Z'

      透過此行程式碼切換至 Places SDK for Android:

          implementation 'com.google.android.libraries.places:places:3.3.0'

  3. 同步處理您的 Gradle 專案。

  4. 將應用程式專案的 minSdkVersion 設為 16 以上。

  5. 更新「Google 技術提供」素材資源:

    @drawable/powered_by_google_light // OLD
    @drawable/places_powered_by_google_light // NEW
    @drawable/powered_by_google_dark // OLD
    @drawable/places_powered_by_google_dark // NEW
    
  6. 建構應用程式。如果您在轉換至 Places SDK for Android 後看到任何建構錯誤,請參閱以下各節的說明,瞭解如何解決這些錯誤。

初始化新的 Places SDK 用戶端

請初始化新的 Places SDK 用戶端,如以下範例所示:

// Add an import statement for the client library.
import com.google.android.libraries.places.api.Places;

...

// Initialize Places.
Places.initialize(getApplicationContext(), apiKey);

// Create a new Places client instance.
PlacesClient placesClient = Places.createClient(this);

狀態碼

每秒查詢限制錯誤的狀態碼已變更。每秒查詢限制錯誤現在會透過 PlaceStatusCodes.OVER_QUERY_LIMIT 傳回。已無其他 QPD 限制。

已新增下列狀態碼:

  • REQUEST_DENIED:要求遭拒。可能原因如下:

    • 未提供 API 金鑰。
    • 提供的 API 金鑰無效。
    • 您尚未在 Cloud 控制台中啟用 Places API。
    • 提供的 API 金鑰限制有誤。
  • INVALID_REQUEST:因缺少引數或引數無效,要求無效。

  • NOT_FOUND:找不到指定要求的結果。

新方法

新版 Places SDK for Android 推出了專為一致性設計的全新方法。所有新方法均具備下列優勢:

  • 端點不再使用 get 動詞。
  • 要求和回應物件的名稱與對應用戶端方法的名稱相同。
  • 要求物件現在有建構工具;必要的參數會以要求建構工具參數的形式傳遞。
  • 已停止使用緩衝區。

本節將說明新方法及其運作方式。

依 ID 擷取地點

使用 fetchPlace() 取得特定地點的詳細資料。fetchPlace() 函式與 getPlaceById() 類似。

請按照下列步驟擷取地點:

  1. 呼叫 fetchPlace(),傳遞 FetchPlaceRequest 物件指定地點 ID 和欄位清單,指定要傳回的地點資料。

    // Define a Place ID.
    String placeId = "INSERT_PLACE_ID_HERE";
    
    // Specify the fields to return.
    List<Place.Field> placeFields = Arrays.asList(Place.Field.ID, Place.Field.NAME);
    
    // Construct a request object, passing the place ID and fields array.
    FetchPlaceRequest request = FetchPlaceRequest.builder(placeId, placeFields)
            .build();
    
    
  2. 呼叫 addOnSuccessListener() 來處理 FetchPlaceResponse。系統會傳回單一 Place 結果。

    // Add a listener to handle the response.
    placesClient.fetchPlace(request).addOnSuccessListener((response) -> {
      Place place = response.getPlace();
      Log.i(TAG, "Place found: " + place.getName());
    }).addOnFailureListener((exception) -> {
        if (exception instanceof ApiException) {
            ApiException apiException = (ApiException) exception;
            int statusCode = apiException.getStatusCode();
            // Handle error with given status code.
            Log.e(TAG, "Place not found: " + exception.getMessage());
        }
    });
    

擷取地點相片

使用 fetchPhoto() 取得地點相片。fetchPhoto() 會傳回地點的相片。我們已簡化要求相片的模式。您現在可以直接透過 Place 物件要求 PhotoMetadata,不再需要另外提出要求。相片的寬度或高度上限為 1600 像素。fetchPhoto() 函式與 getPhoto() 類似。

請按照下列步驟擷取地點相片:

  1. 設定撥打 fetchPlace()。請務必在要求中加入 PHOTO_METADATAS 欄位:

    List<Place.Field> fields = Arrays.asList(Place.Field.PHOTO_METADATAS);
    
  2. 取得 Place 物件 (本範例使用 fetchPlace(),但您也可以使用 findCurrentPlace()):

    FetchPlaceRequest placeRequest = FetchPlaceRequest.builder(placeId, fields).build();
    
  3. 新增 OnSuccessListener 以從 FetchPlaceResponse 中產生的 Place 取得相片中繼資料,然後使用產生的相片中繼資料取得點陣圖和作者資訊文字:

    placesClient.fetchPlace(placeRequest).addOnSuccessListener((response) -> {
        Place place = response.getPlace();
    
        // Get the photo metadata.
        PhotoMetadata photoMetadata = place.getPhotoMetadatas().get(0);
    
        // Get the attribution text.
        String attributions = photoMetadata.getAttributions();
    
        // Create a FetchPhotoRequest.
        FetchPhotoRequest photoRequest = FetchPhotoRequest.builder(photoMetadata)
                .setMaxWidth(500) // Optional.
                .setMaxHeight(300) // Optional.
                .build();
        placesClient.fetchPhoto(photoRequest).addOnSuccessListener((fetchPhotoResponse) -> {
            Bitmap bitmap = fetchPhotoResponse.getBitmap();
            imageView.setImageBitmap(bitmap);
        }).addOnFailureListener((exception) -> {
            if (exception instanceof ApiException) {
                ApiException apiException = (ApiException) exception;
                int statusCode = apiException.getStatusCode();
                // Handle error with given status code.
                Log.e(TAG, "Place not found: " + exception.getMessage());
            }
        });
    });
    

從使用者所在位置尋找地點

使用 findCurrentPlace() 找出使用者裝置的目前位置。findCurrentPlace() 會傳回 PlaceLikelihood 清單,指出使用者裝置最有可能的所在位置。findCurrentPlace() 函式與 getCurrentPlace() 類似。

如要取得使用者裝置的目前位置,請按照下列步驟操作:

  1. 請確認您的應用程式要求 ACCESS_FINE_LOCATIONACCESS_WIFI_STATE 權限。使用者必須授予權限,才能存取目前裝置的所在位置。詳情請參閱「要求應用程式權限」。

  2. 建立 FindCurrentPlaceRequest,包括要傳回的地點資料類型清單。

      // Use fields to define the data types to return.
      List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME);
    
      // Use the builder to create a FindCurrentPlaceRequest.
      FindCurrentPlaceRequest request =
              FindCurrentPlaceRequest.builder(placeFields).build();
    
  3. 呼叫 findCurrentPlace 並處理回應,先檢查使用者是否已授予使用裝置位置資訊的權限。

      // Call findCurrentPlace and handle the response (first check that the user has granted permission).
      if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
          placesClient.findCurrentPlace(request).addOnSuccessListener(((response) -> {
              for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
                  Log.i(TAG, String.format("Place '%s' has likelihood: %f",
                          placeLikelihood.getPlace().getName(),
                          placeLikelihood.getLikelihood()));
                  textView.append(String.format("Place '%s' has likelihood: %f\n",
                          placeLikelihood.getPlace().getName(),
                          placeLikelihood.getLikelihood()));
              }
          })).addOnFailureListener((exception) -> {
              if (exception instanceof ApiException) {
                  ApiException apiException = (ApiException) exception;
                  Log.e(TAG, "Place not found: " + apiException.getStatusCode());
              }
          });
      } else {
          // A local method to request required permissions;
          // See https://developer.android.com/training/permissions/requesting
          getLocationPermission();
      }
    

尋找自動完成預測

使用 findAutocompletePredictions() 可根據使用者搜尋查詢傳回地點預測結果。findAutocompletePredictions() 函式與 getAutocompletePredictions() 類似。

下例示範如何呼叫 findAutocompletePredictions()

// Create a new token for the autocomplete session. Pass this to FindAutocompletePredictionsRequest,
// and once again when the user makes a selection (for example when calling fetchPlace()).
AutocompleteSessionToken token = AutocompleteSessionToken.newInstance();
// Create a RectangularBounds object.
RectangularBounds bounds = RectangularBounds.newInstance(
  new LatLng(-33.880490, 151.184363),
  new LatLng(-33.858754, 151.229596));
// Use the builder to create a FindAutocompletePredictionsRequest.
FindAutocompletePredictionsRequest request = FindAutocompletePredictionsRequest.builder()
// Call either setLocationBias() OR setLocationRestriction().
   .setLocationBias(bounds)
   //.setLocationRestriction(bounds)
   .setCountry("au")
   .setTypesFilter(Arrays.asList(PlaceTypes.ADDRESS))
   .setSessionToken(token)
   .setQuery(query)
   .build();

placesClient.findAutocompletePredictions(request).addOnSuccessListener((response) -> {
   for (AutocompletePrediction prediction : response.getAutocompletePredictions()) {
       Log.i(TAG, prediction.getPlaceId());
       Log.i(TAG, prediction.getPrimaryText(null).toString());
   }
}).addOnFailureListener((exception) -> {
   if (exception instanceof ApiException) {
       ApiException apiException = (ApiException) exception;
       Log.e(TAG, "Place not found: " + apiException.getStatusCode());
   }
});

工作階段符記

工作階段符記會將使用者搜尋的查詢和選取階段歸入不同的工作階段,以用於計費。建議您在所有自動完成工作階段使用工作階段符記。工作階段是從使用者輸入查詢時開始,到使用者選取地點時結束。在每個工作階段中,使用者可以輸入多筆查詢,最終選擇一個地點。工作階段結束後,符記就會失效;應用程式必須為每個工作階段產生新的符記。

欄位遮罩

在傳回 Place Details 的方法中,您必須指定要透過每項要求傳回的地點資料類型。這可確保您只要求並支付實際使用的資料。

如要指定要傳回的資料類型,請在 FetchPlaceRequest 中傳遞 Place.Field 的陣列,如以下範例所示:

// Include address, ID, and phone number.
List<Place.Field> placeFields = Arrays.asList(Place.Field.ADDRESS,
                                              Place.Field.ID,
                                              Place.Field.PHONE_NUMBER);

您可以使用下列一或多個欄位:

  • Place.Field.ADDRESS
  • Place.Field.ID
  • Place.Field.LAT_LNG
  • Place.Field.NAME
  • Place.Field.OPENING_HOURS
  • Place.Field.PHONE_NUMBER
  • Place.Field.PHOTO_METADATAS
  • Place.Field.PLUS_CODE
  • Place.Field.PRICE_LEVEL
  • Place.Field.RATING
  • Place.Field.TYPES
  • Place.Field.USER_RATINGS_TOTAL
  • Place.Field.VIEWPORT
  • Place.Field.WEBSITE_URI

進一步瞭解地點資料 SKU

地點挑選器和自動完成更新

本節說明地點介面集小工具 (地點挑選器和自動完成) 的異動內容。

程式輔助自動完成

自動完成功能有以下變更:

  • PlaceAutocomplete 已重新命名為 Autocomplete
    • PlaceAutocomplete.getPlace 已重新命名為 Autocomplete.getPlaceFromIntent
    • PlaceAutocomplete.getStatus 已重新命名為 Autocomplete.getStatusFromIntent
  • PlaceAutocomplete.RESULT_ERROR 已重新命名為 AutocompleteActivity.RESULT_ERROR (自動完成片段的錯誤處理方式「沒有」變更)。

地點挑選器

地點挑選程式已於 2019 年 1 月 29 日淘汰。該功能已於 2019 年 7 月 29 日停用,不再提供使用。若持續使用,就會收到錯誤訊息。新的 SDK 不支援地點選擇器。

自動完成小工具

已更新自動完成小工具:

  • 已從所有類別中移除 Place 前置字串。
  • 新增工作階段符記支援功能。小工具會在背景自動為您管理權杖。
  • 新增對欄位遮罩的支援,讓您選擇要在使用者選取項目後要傳回的地點資料類型。

以下各節將說明如何在專案中新增自動完成小工具。

嵌入 AutocompleteFragment

如要新增自動完成片段,請按照下列步驟操作:

  1. 在活動的 XML 版面配置中新增片段,如以下範例所示。

    <fragment
      android:id="@+id/autocomplete_fragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:name=
    "com.google.android.libraries.places.widget.AutocompleteSupportFragment"
      />
    
  2. 如要在活動中新增自動完成小工具,請按照下列步驟操作:

    • 初始化 Places,並傳遞應用程式結構定義和您的 API 金鑰。
    • 初始化 AutocompleteSupportFragment
    • 呼叫 setPlaceFields() 來表示需要取得的地點資料類型。
    • 新增 PlaceSelectionListener 以使用結果處理某些作業,以及處理任何可能發生的錯誤。

    以下範例說明如何在活動中新增 Autocomplete 小工具:

    /**
     * Initialize Places. For simplicity, the API key is hard-coded. In a production
     * environment we recommend using a secure mechanism to manage API keys.
     */
    if (!Places.isInitialized()) {
        Places.initialize(getApplicationContext(), "YOUR_API_KEY");
    }
    
    // Initialize the AutocompleteSupportFragment.
    AutocompleteSupportFragment autocompleteFragment = (AutocompleteSupportFragment)
            getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment);
    
    autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
    
    autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
        @Override
        public void onPlaceSelected(Place place) {
            // TODO: Get info about the selected place.
            Log.i(TAG, "Place: " + place.getName() + ", " + place.getId());
        }
    
        @Override
        public void onError(Status status) {
            // TODO: Handle the error.
            Log.i(TAG, "An error occurred: " + status);
        }
    });
    

使用意圖啟動自動完成活動

  1. 初始化 Places,並傳遞應用程式結構定義和您的 API 金鑰
  2. 使用 Autocomplete.IntentBuilder 建立意圖,並傳遞所需的 PlaceAutocomplete 模式 (全螢幕或重疊)。意圖必須呼叫 startActivityForResult,並傳入可識別意圖的要求代碼。
  3. 覆寫 onActivityResult 回呼以接收所選地點。

以下範例說明如何使用意圖啟動自動完成功能,然後處理結果:

    /**
     * Initialize Places. For simplicity, the API key is hard-coded. In a production
     * environment we recommend using a secure mechanism to manage API keys.
     */
    if (!Places.isInitialized()) {
        Places.initialize(getApplicationContext(), "YOUR_API_KEY");
    }

    ...

    // Set the fields to specify which types of place data to return.
    List<Place.Field> fields = Arrays.asList(Place.Field.ID, Place.Field.NAME);

    // Start the autocomplete intent.
    Intent intent = new Autocomplete.IntentBuilder(
            AutocompleteActivityMode.FULLSCREEN, fields)
            .build(this);
    startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE);

    ...

    /**
     * Override the activity's onActivityResult(), check the request code, and
     * do something with the returned place data (in this example its place name and place ID).
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == AUTOCOMPLETE_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                Place place = Autocomplete.getPlaceFromIntent(data);
                Log.i(TAG, "Place: " + place.getName() + ", " + place.getId());
            } else if (resultCode == AutocompleteActivity.RESULT_ERROR) {
                // TODO: Handle the error.
                Status status = Autocomplete.getStatusFromIntent(data);
                Log.i(TAG, status.getStatusMessage());
            } else if (resultCode == RESULT_CANCELED) {
                // The user canceled the operation.
            }
        }
    }

地點挑選器已無法使用

地點挑選程式已於 2019 年 1 月 29 日淘汰。該功能已於 2019 年 7 月 29 日停用,不再提供使用。若持續使用,就會收到錯誤訊息。新的 SDK 不支援地點選擇器。