迁移到新的 Places SDK 客户端

本指南介绍了“地方信息” 和最新的独立版 Places SDK for Android。 如果您一直在使用地点兼容性库,而不是迁移到 新独立版 Places SDK for Android,本指南将为您介绍 如何更新项目以使用新版 Places SDK for Android。

访问 Places SDK for Android 中的功能和 bug 修复的唯一方式 将使用 Places SDK for Android。 Google 建议从兼容性库更新为新的 尽快发布 Places SDK for Android 版本。

发生了哪些变化?

主要变更如下:

  • 新版 Places SDK for Android 已分发 作为静态客户端库在 2019 年 1 月之前,Places SDK for Android 是通过 Google Play 服务提供的。从那以后,地点就开始 提供兼容性库,以轻松过渡到新的 Places SDK for Android。
  • 我们提供了全新的方法
  • 返回地点的方法现在支持字段掩码 。您可以使用字段掩码指定要接收哪些类型的地点数据 return。
  • 改进了用于报告错误的状态代码
  • 自动补全现在支持会话令牌
  • 地点选择器不再可用

关于地点兼容性库

2019 年 1 月,随着独立 Places SDK for Android 1.0 版的发布, Google 提供了一个兼容性库,以帮助进行迁移 来自已弃用的 Places SDK for Android 的 Google Play 服务版本 (com.google.android.gms:play-services-places)。

此兼容性库是临时提供的,用于重定向和转换 针对 Google Play 服务版本的新独立 API 调用 直到开发者可以将其代码迁移到 独立 SDK。对于每个版本的 Places SDK for Android, 1.0 至 2.6.0 版(相应的版本) 发布了等效地点兼容性库的功能, 功能

冻结并弃用地点兼容性库

Places SDK for Android 的所有版本的兼容性库 自 2022 年 3 月 31 日起弃用。2.6.0 版是 地点兼容性库。只有这样,您才能在“地点”中使用各种功能和问题修复 SDK for Android 2.6.0 以上版本将使用 Places SDK for Android。

Google 建议您迁移到 Places SDK for Android 以使用 2.6.0 以上版本的新功能和重要问题修复。 如果您目前使用的是兼容性库,请按照 安装 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. 如果您目前使用的是 Play 服务版本的 Places SDK for Android

    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);

状态代码

QPS 限制错误的状态代码已更改。QPS 限制错误现为 通过 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() 会返回地点的照片。模式 请求照片的流程已经简化了您现在可以请求PhotoMetadata了 直接从 Place 对象获取数据;不再需要单独请求。 照片的宽度或高度上限为 1600 像素。fetchPhoto() 函数 与 getPhoto() 类似。

请按照以下步骤提取地点照片:

  1. 设置对 fetchPlace() 的调用。请确保添加 请求中的 PHOTO_METADATAS 字段:

    List<Place.Field> fields = Arrays.asList(Place.Field.PHOTO_METADATAS);
    
  2. 获取地点对象(此示例使用的是 fetchPlace(),但您也可以使用 findCurrentPlace()):

    FetchPlaceRequest placeRequest = FetchPlaceRequest.builder(placeId, fields).build();
    
  3. 添加 OnSuccessListener 以从生成的结果中获取照片元数据 Place(位于 FetchPlaceResponse 中),然后使用生成的照片元数据 获取位图和提供方说明文本:

    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,包括要指定地点数据类型的列表 return。

      // 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.Field FetchPlaceRequest,如以下示例所示:

// 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 (自动补全 fragment 的错误处理方式没有发生变化)。

地点选取器

地点选择器已于 2019 年 1 月 29 日弃用。它已关闭 。继续使用将导致 错误消息。新 SDK 不支持地点选择器。

自动补全 widget

自动补全 widget 已更新:

  • 已从所有类中移除 Place 前缀。
  • 新增了对会话令牌的支持。该微件可为您管理令牌 自动在后台运行
  • 添加了对字段掩码的支持,可让您选择地点类型 在用户做出选择后返回的数据

以下各部分展示了如何为项目添加自动补全 widget。

嵌入 AutocompleteFragment

如需添加自动补全 fragment,请按以下步骤操作:

  1. 向 activity 的 XML 布局中添加 fragment,如下所示 示例。

    <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. 如需向 activity 添加自动补全 widget,请按以下步骤操作:

    • 初始化 Places,以传递应用上下文和您的 API 密钥。
    • 初始化 AutocompleteSupportFragment
    • 调用 setPlaceFields() 以指明所需的地点数据类型 。
    • 添加 PlaceSelectionListener 以对结果执行某些操作,以及 处理可能发生的任何错误。

    以下示例展示了如何向 activity 添加自动补全 widget:

    /**
     * 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);
        }
    });
    

使用 intent 启动自动补全 activity

  1. 初始化 Places,以传递应用上下文和您的 API 密钥
  2. 使用 Autocomplete.IntentBuilder 创建 intent,并传递所需的 PlaceAutocomplete 模式(全屏或叠加层)。intent 必须调用 startActivityForResult,从而传入一个请求代码,该代码用于标识您的 intent。
  3. 替换 onActivityResult 回调以接收所选地点。

以下示例展示了如何使用 intent 启动自动补全功能, 然后处理结果:

    /**
     * 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 日弃用。它已关闭 。继续使用将导致 错误消息。新 SDK 不支持地点选择器。