遷移至新的 Places SDK 用戶端

本指南說明 Places 相容性程式庫與 Places SDK for Android 新獨立版本之間的變更。如果您一直使用 Places 相容性程式庫,而非遷移至新版的獨立版本 Places SDK for Android,本指南將說明如何更新專案,以便使用新版的 Places SDK for Android。

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

異動內容

主要變更內容如下:

  • 新版 Places SDK for Android 會以靜態用戶端程式庫的形式發布。在 2019 年 1 月之前,Android 版 Places SDK 是透過 Google Play 服務提供。自那時起,我們已提供 Places 相容性程式庫,方便您轉換至新的 Places SDK for Android。
  • 全新方法
  • 欄位遮罩現在支援傳回地點詳細資料的方法。您可以使用欄位遮罩指定要傳回哪些類型的地點資料。
  • 用於回報錯誤的狀態碼已改善。
  • Autocomplete 現已支援工作階段權杖
  • Place Picker 已停用

關於 Places 相容性程式庫

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

這個相容性程式庫是暫時提供的,可將針對 Google Play 服務版本的 API 呼叫重新導向及轉譯為新的獨立版本,直到開發人員能夠將程式碼遷移至獨立 SDK 並使用新名稱為止。針對從 1.0 版到 2.6.0 版的每個 Android 版 Places SDK,我們都會發布相應的 Places 相容性程式庫版本,提供等同的功能。

凍結並淘汰 Places 相容性程式庫

自 2022 年 3 月 31 日起,所有版本的 Android 版 Places SDK 相容性程式庫皆已淘汰。2.6.0 版是 Places 相容性程式庫的最後一個版本。如要存取 2.6.0 以上版本的 Places SDK for Android 中的功能和錯誤修正,唯一方法就是使用 Places SDK for Android。

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

安裝用戶端程式庫

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

使用 Maven 將 Places SDK for Android 新增至 Android Studio 專案:

  1. 如果您目前使用 Places 相容性程式庫

    1. dependencies 部分中取代以下行:

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

      使用這行程式碼切換至 Android 版 Places SDK:

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

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

    1. dependencies 部分中取代以下行:

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

      使用這行程式碼切換至 Android 版 Places SDK:

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

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

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

  5. 更新「Powered by 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. 建構應用程式。如果您在轉換為 Android 版 Places SDK 時看到任何建構錯誤,請參閱下列章節,瞭解如何解決這些錯誤。

初始化新的 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);

狀態碼

QPS 限制錯誤的狀態碼已變更。每秒查詢次數限制錯誤現在會透過 PlaceStatusCodes.OVER_QUERY_LIMIT 傳回。不再有 QPD 限制。

新增了下列狀態碼:

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

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

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

新方法

新版 Places SDK for Android 推出了全新的設計方法,可確保一致性。所有新方法都遵循以下規則:

  • Endpoints 不再使用 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.DISPLAY_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.DISPLAY_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());
   }
});

工作階段符記

為方便結算,工作階段權杖會將使用者搜尋的查詢和選取階段分組為個別工作階段。建議您在所有自動完成工作階段使用工作階段符記。工作階段會在使用者開始輸入查詢時開始,並在使用者選取地點時結束。每個工作階段可包含多個查詢,接著是一次地點選取。在工作階段結束後,權杖就會失效;應用程式必須為每個工作階段產生新的權杖。

欄位遮罩

在傳回地點詳細資料的方法中,您必須指定每項要求要傳回哪些類型的地點資料。這有助於確保您只要求 (及付費) 實際會使用的資料。

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

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

如需可在欄位遮罩中使用的欄位清單,請參閱「Place Data Fields (New) 」一文。

進一步瞭解 Places Data SKU

Place Picker 和 Autocomplete 更新

本節將說明地點小工具 (Place Picker 和 Autocomplete) 的變更。

程式輔助自動完成

我們對 autocomplete進行了以下變更:

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

地點挑選器

我們已於 2019 年 1 月 29 日淘汰 Place Picker。這項服務已於 2019 年 7 月 29 日關閉,目前無法使用。繼續使用會導致系統顯示錯誤訊息。新的 SDK 不支援 Place Picker。

Autocomplete 小工具

自動完成小工具已更新:

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

以下各節將說明如何在專案中新增 Autocomplete 小工具。

嵌入 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. 如要將 Autocomplete 小工具新增至活動,請按照下列步驟操作:

    • 初始化 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.DISPLAY_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.DISPLAY_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.
            }
        }
    }

Place Picker 已無法使用

我們已於 2019 年 1 月 29 日淘汰 Place Picker。這項服務已於 2019 年 7 月 29 日關閉,目前無法使用。繼續使用會導致系統顯示錯誤訊息。新版 SDK 不支援 Place Picker。