You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让placeRequest获取findAutocompletePredictions的首个预测结果?

解决Google Places API异步操作的结果传递问题

这个问题我太熟了!Google Places API的异步操作确实容易踩这个坑——因为findAutocompletePredictions是异步执行的,你写在外面的fetchPlaceRequest代码会先运行,这时候不仅拿不到预测结果,用全局变量也会因为异步时序问题变成null。给你几个靠谱的解决办法:

方案一:直接嵌套异步回调(最直观的改法)

把获取Place的逻辑直接放到自动补全的成功回调里,这样确保拿到第一个预测结果后再执行fetchPlace操作:

class PlacesApi(context: Context, apiKey: String) {
    private val placesClient: PlacesClient
    private val token: AutocompleteSessionToken
    private lateinit var place: Place

    init {
        Places.initialize(context, apiKey)
        placesClient = Places.createClient(context)
        token = AutocompleteSessionToken.newInstance()
    }

    fun getCityPlaceId(inputString: String) {
        val userInputRequest = FindAutocompletePredictionsRequest.builder()
            .setTypeFilter(TypeFilter.CITIES)
            .setSessionToken(token)
            .setQuery(inputString)
            .build()

        placesClient.findAutocompletePredictions(userInputRequest)
            .addOnSuccessListener { response: FindAutocompletePredictionsResponse ->
                // 取第一个预测结果,为空时做容错处理
                val firstPrediction = response.autocompletePredictions.firstOrNull()
                firstPrediction?.let { prediction ->
                    Log.d("Place ID", prediction.placeId)
                    Log.d("Place City", prediction.getPrimaryText(null).toString())
                    Log.d("Place Country", prediction.getSecondaryText(null).toString())

                    // 拿到placeId后再创建请求并获取Place详情
                    val placeFields: List<Place.Field> = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS)
                    val placeRequest = FetchPlaceRequest.newInstance(prediction.placeId, placeFields)

                    placesClient.fetchPlace(placeRequest)
                        .addOnSuccessListener { fetchPlaceResponse: FetchPlaceResponse ->
                            place = fetchPlaceResponse.place
                            Log.i("Place coordinates: ", place.latLng?.longitude.toString())
                            Log.i("Place coordinates: ", place.latLng?.latitude.toString())
                            Log.i("Place name: ", place.name.toString())
                            Log.i("Place address: ", place.address.toString())
                        }.addOnFailureListener {
                            if (it is ApiException) {
                                Log.e("Place not found", "Place not found " + it.message)
                            }
                        }
                } ?: run {
                    Log.w("Prediction", "No autocomplete predictions found")
                }
            }
            .addOnFailureListener {
                Log.e("Autocomplete error", "Failed to get predictions: ${it.message}")
            }
    }
}

方案二:使用回调函数解耦代码

如果不想嵌套太多回调,可以给getCityPlaceId添加一个回调参数,拿到最终Place结果后通知调用者:

// 先定义回调接口
interface PlaceResultCallback {
    fun onPlaceLoaded(place: Place)
    fun onError(message: String)
}

class PlacesApi(context: Context, apiKey: String) {
    private val placesClient: PlacesClient
    private val token: AutocompleteSessionToken

    init {
        Places.initialize(context, apiKey)
        placesClient = Places.createClient(context)
        token = AutocompleteSessionToken.newInstance()
    }

    fun getCityPlaceId(inputString: String, callback: PlaceResultCallback) {
        val userInputRequest = FindAutocompletePredictionsRequest.builder()
            .setTypeFilter(TypeFilter.CITIES)
            .setSessionToken(token)
            .setQuery(inputString)
            .build()

        placesClient.findAutocompletePredictions(userInputRequest)
            .addOnSuccessListener { response: FindAutocompletePredictionsResponse ->
                val firstPrediction = response.autocompletePredictions.firstOrNull()
                firstPrediction?.let { prediction ->
                    val placeFields: List<Place.Field> = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS)
                    val placeRequest = FetchPlaceRequest.newInstance(prediction.placeId, placeFields)

                    placesClient.fetchPlace(placeRequest)
                        .addOnSuccessListener { fetchPlaceResponse ->
                            callback.onPlaceLoaded(fetchPlaceResponse.place)
                        }.addOnFailureListener {
                            val errorMsg = if (it is ApiException) "Place not found: ${it.message}" else "Failed to fetch place: ${it.message}"
                            callback.onError(errorMsg)
                        }
                } ?: callback.onError("No autocomplete predictions found")
            }
            .addOnFailureListener {
                callback.onError("Failed to get autocomplete predictions: ${it.message}")
            }
    }
}

// 调用示例
// placesApi.getCityPlaceId("Beijing", object : PlaceResultCallback {
//     override fun onPlaceLoaded(place: Place) {
//         // 处理拿到的Place对象
//     }
//
//     override fun onError(message: String) {
//         // 处理错误
//     }
// })

方案三:用Kotlin协程简化异步代码(推荐)

Kotlin的协程可以把异步操作转换成类似同步的写法,避免回调地狱,代码更简洁易读:

首先确保你的项目已经引入协程依赖,然后修改代码:

import com.google.android.gms.tasks.await

class PlacesApi(context: Context, apiKey: String) {
    private val placesClient: PlacesClient
    private val token: AutocompleteSessionToken

    init {
        Places.initialize(context, apiKey)
        placesClient = Places.createClient(context)
        token = AutocompleteSessionToken.newInstance()
    }

    // 把方法改成挂起函数,需要在协程作用域内调用
    suspend fun getCityPlaceId(inputString: String): Place? {
        return try {
            // 等待自动补全结果返回
            val userInputRequest = FindAutocompletePredictionsRequest.builder()
                .setTypeFilter(TypeFilter.CITIES)
                .setSessionToken(token)
                .setQuery(inputString)
                .build()
            val autocompleteResponse = placesClient.findAutocompletePredictions(userInputRequest).await()
            
            val firstPrediction = autocompleteResponse.autocompletePredictions.firstOrNull()
            firstPrediction?.let { prediction ->
                // 等待Place详情结果返回
                val placeFields: List<Place.Field> = listOf(Place.Field.ID, Place.Field.NAME, Place.Field.LAT_LNG, Place.Field.ADDRESS)
                val placeRequest = FetchPlaceRequest.newInstance(prediction.placeId, placeFields)
                val fetchPlaceResponse = placesClient.fetchPlace(placeRequest).await()
                fetchPlaceResponse.place
            } ?: run {
                Log.w("Prediction", "No autocomplete predictions found")
                null
            }
        } catch (e: Exception) {
            val errorMsg = if (e is ApiException) "Place error: ${e.message}" else "Async error: ${e.message}"
            Log.e("PlacesApi", errorMsg)
            null
        }
    }
}

// 调用示例(需要在协程作用域内,比如Activity的lifecycleScope)
// lifecycleScope.launch {
//     val place = placesApi.getCityPlaceId("Shanghai")
//     place?.let {
//         // 处理Place对象
//     }
// }

这三种方案里,协程的写法最优雅,也最容易维护,推荐优先使用。

内容的提问来源于stack exchange,提问作者Jax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 10:22:29