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

新Places SDK中PendingResult await()等效方案及循环阻塞问题

解决新Places SDK中无法阻塞等待Place查询结果的问题

你的痛点我太懂了——旧SDK里用PendingResult.await()可以很轻松地在循环里逐个同步查询Place,结果新SDK全改成异步回调了,直接导致循环跑完了,查询结果还没回来,功能完全失效。不过别担心,我们可以用几种方式把异步操作“转”成同步等待的逻辑,让你的代码按预期执行。

方案一:用Java的CompletableFuture包装异步回调(Java项目适用)

我们可以把fetchPlace的异步回调塞进CompletableFuture里,然后用get()方法阻塞当前线程,直到拿到Place结果再继续循环。

给你写个示例代码:

// 遍历你的Office Place ID列表
for (String officePlaceId : officePlaceIdList) {
    // 创建一个CompletableFuture来承接异步结果
    CompletableFuture<Place> placeFuture = new CompletableFuture<>();

    // 构建查询请求,这里只请求经纬度字段就够了
    FetchPlaceRequest request = FetchPlaceRequest.newInstance(officePlaceId, Arrays.asList(Place.Field.LAT_LNG));
    
    placesClient.fetchPlace(request)
        .addOnSuccessListener(response -> {
            // 拿到结果后,把它传给Future
            placeFuture.complete(response.getPlace());
        })
        .addOnFailureListener(exception -> {
            // 处理失败情况,比如把异常传给Future
            placeFuture.completeExceptionally(exception);
        });

    try {
        // 阻塞当前线程,直到Future拿到结果
        Place officePlace = placeFuture.get();
        // 这里执行你的距离判断逻辑
        LatLng officeLatLng = officePlace.getLatLng();
        float distance = SphericalUtil.computeDistanceBetween(userSelectedLatLng, officeLatLng);
        if (distance < 50) {
            // 找到符合条件的Office,你可以在这里做后续处理,比如跳出循环
            break;
        }
    } catch (InterruptedException | ExecutionException e) {
        // 处理异常,比如打印日志或者跳过当前这个Place
        e.printStackTrace();
    }
}

⚠️ 重要提醒:绝对不要在UI线程跑这段代码! get()方法会阻塞线程,UI线程被阻塞超过5秒就会触发ANR(应用无响应)。你得把这段逻辑放到后台线程里,比如用ExecutorService、AsyncTask或者WorkManager来执行。

方案二:用Kotlin协程实现同步写法(Kotlin项目适用)

如果你的项目是用Kotlin写的,那协程简直是解决这个问题的完美工具——它能让你用同步的代码风格写异步逻辑,完全不用回调嵌套。

示例代码如下:

// 先切换到IO调度器的协程里执行,避免阻塞UI
CoroutineScope(Dispatchers.IO).launch {
    for (officePlaceId in officePlaceIdList) {
        val request = FetchPlaceRequest.newInstance(officePlaceId, listOf(Place.Field.LAT_LNG))
        try {
            // 用await()等待查询结果,这会挂起协程但不会阻塞线程
            val response = placesClient.fetchPlace(request).await()
            val officePlace = response.place
            val distance = SphericalUtil.computeDistanceBetween(userSelectedLatLng, officePlace.latLng)
            if (distance < 50) {
                // 找到目标Office后,如果需要更新UI,切换回主线程
                withContext(Dispatchers.Main) {
                    // 这里写UI相关的逻辑,比如提示用户找到附近办公室
                }
                break
            }
        } catch (e: Exception) {
            // 处理查询失败的情况
            e.printStackTrace()
        }
    }
}

要注意,这里用到的await()是Google为Places SDK的Task类提供的Kotlin扩展函数,你需要确保项目里引入了正确的依赖:

implementation "com.google.android.gms:play-services-places:19.0.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"

备选方案:不阻塞,改用批量异步处理

如果你不想阻塞线程,也可以一次性发起所有Place查询,等所有结果都回来后再统一判断距离。这种方式速度更快,但需要处理多个异步任务的结果:

用Java的CountDownLatch实现的示例:

// 初始化计数器,数值等于Place ID的数量
CountDownLatch latch = new CountDownLatch(officePlaceIdList.size());
// 用线程安全的列表存结果
List<Place> officePlaces = Collections.synchronizedList(new ArrayList<>());

for (String officePlaceId : officePlaceIdList) {
    FetchPlaceRequest request = FetchPlaceRequest.newInstance(officePlaceId, Arrays.asList(Place.Field.LAT_LNG));
    placesClient.fetchPlace(request)
        .addOnSuccessListener(response -> {
            officePlaces.add(response.getPlace());
            latch.countDown(); // 每完成一个请求,计数器减1
        })
        .addOnFailureListener(exception -> {
            latch.countDown(); // 失败也要减计数器,避免一直阻塞
        });
}

try {
    // 阻塞直到所有请求完成
    latch.await();
    // 现在遍历所有拿到的Place,判断距离
    for (Place officePlace : officePlaces) {
        float distance = SphericalUtil.computeDistanceBetween(userSelectedLatLng, officePlace.getLatLng());
        if (distance < 50) {
            // 找到符合条件的Office
            break;
        }
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

这种方式的好处是所有请求并行发起,效率更高;缺点是必须等所有请求都完成才能开始判断,而且要注意线程安全问题。

最后再强调一遍:不管用哪种方案,都不能在UI线程执行阻塞操作,一定要放到后台线程或者协程的IO调度器里!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:18:50