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

Android中Google Place Autocomplete重复调用出现shutdown错误的解决

问题描述

为API 33的Android应用开发Google Place Autocomplete功能,需求是让用户输入并选择地址,之后获取该地点信息并在地图上展示。实现流程为:初始化API,指定用户选择后返回的地点数据字段,通过Intent调用AutocompleteActivityMode.FULLSCREEN模式的AutocompleteActivity,用户选择后获取经纬度。

目前自动补全功能正常,地址可显示且能获取到位置,但从第二次调用AutocompleteActivity搜索新地址开始,日志中反复出现错误提示:Make sure to call shutdown()/shutdownNow() and wait until awaitTermination() returns true。经调研推测原因是API与应用间的通信通道未正确关闭,需要执行shutdown操作,但未在官方文档中找到相关说明,需解决该错误。

错误日志

E/zzbfw: *~*~*~ Previous channel {0} was not shutdown properly!!! ~*~*~*
        Make sure to call shutdown()/shutdownNow() and wait until awaitTermination() returns true.
    java.lang.RuntimeException: ManagedChannel allocation site
        at com.google.android.libraries.places.internal.zzbfv.<init>(com.google.android.libraries.places:places@@3.3.0:3)
        at com.google.android.libraries.places.internal.zzbfw.<init>(com.google.android.libraries.places:places@@3.3.0:2)
        at com.google.android.libraries.places.internal.zzbfu.zza(com.google.android.libraries.places:places@@3.3.0:18)
        at com.google.android.libraries.places.internal.zzatj.zza(com.google.android.libraries.places:places@@3.3.0:1)
        at com.google.android.libraries.places.internal.zzhr.zza(com.google.android.libraries.places:places@@3.3.0:24)
        at com.google.android.libraries.places.api.Places.zza(com.google.android.libraries.places:places@@3.3.0:7)
        at com.google.android.libraries.places.widget.internal.ui.zzh.<init>(com.google.android.libraries.places:places@@3.3.0:7)
        at com.google.android.libraries.places.widget.AutocompleteActivity.onCreate(com.google.android.libraries.places:places@@3.3.0:12)
        at android.app.Activity.performCreate(Activity.java:7136)
        at android.app.Activity.performCreate(Activity.java:7127)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

现有代码

主逻辑代码

if (id == R.id.action_search_address) {

    if(!Places.isInitialized()){

        // 定义Places API密钥
        String apiKey = "Mykey";
       
        // 初始化SDK
        Places.initialize(getApplicationContext(), apiKey);

    }

    List<Place.Field> fields = Arrays.asList(Place.Field.ADDRESS, Place.Field.LAT_LNG);

    // 启动自动补全Intent
    Intent intent = new Autocomplete.IntentBuilder(AutocompleteActivityMode.FULLSCREEN, fields)
            .build(this);

    startAutocomplete.launch(intent);

}

return super.onOptionsItemSelected(item);

}

private final ActivityResultLauncher<Intent> startAutocomplete = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        result -> {
            if (result.getResultCode() == Activity.RESULT_OK) {
                Intent intent = result.getData();
                if (intent != null) {
                    Place place = Autocomplete.getPlaceFromIntent(intent);
                    Log.i(TAG_LOG,  "地址: " + place.getAddress() + ", 经度: " + place.getLatLng().longitude + " 纬度: " + place.getLatLng().latitude);

                    mapWebView.evaluateJavascript("viewAddress(" + place.getLatLng().latitude + "," + place.getLatLng().longitude + ", \"" + place.getAddress() + "\")", null);

                }
            } else if (result.getResultCode() == Activity.RESULT_CANCELED) {
                // 用户取消操作
                Log.i(TAG_LOG,  "用户取消自动补全");

            }

        });

API配置文件

settings.gradle

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

build.gradle(项目级)

buildscript {
    repositories {
        jcenter()
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.2.1'
        classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"

       
    }
}

allprojects {
    repositories {
        jcenter()
        google()
        mavenCentral()
    }
}

build.gradle(模块级)依赖

implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.21"))
implementation 'com.google.android.libraries.places:places:3.3.0'
修复方案

这个错误是Places SDK内部的gRPC通信通道重复初始化未正确关闭导致的,虽然官方文档未明确提及,但可通过显式管理Places客户端的生命周期来解决:

  • 步骤1:统一初始化时机
    将Places SDK的初始化移至Activity的onCreate方法中,确保仅初始化一次,避免每次点击搜索时重复判断初始化:

    private PlacesClient placesClient;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        // 仅初始化一次Places SDK
        if (!Places.isInitialized()) {
            String apiKey = "Mykey";
            Places.initialize(getApplicationContext(), apiKey);
        }
        // 创建Places客户端实例
        placesClient = Places.createClient(this);
    }
    
  • 步骤2:在生命周期结束时关闭客户端
    在Activity的onDestroy方法中,显式关闭Places客户端,确保通信通道被正确释放。注意awaitTermination是阻塞方法,需在子线程执行:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (placesClient != null) {
            placesClient.shutdown();
            new Thread(() -> {
                try {
                    // 等待5秒让通道关闭,超时则强制关闭
                    if (!placesClient.awaitTermination(5, TimeUnit.SECONDS)) {
                        placesClient.shutdownNow();
                        if (!placesClient.awaitTermination(5, TimeUnit.SECONDS)) {
                            Log.e(TAG_LOG, "PlacesClient 关闭失败");
                        }
                    }
                } catch (InterruptedException e) {
                    placesClient.shutdownNow();
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
    }
    
  • 步骤3:简化搜索点击逻辑
    移除搜索点击事件中的初始化判断,直接启动AutocompleteActivity:

    if (id == R.id.action_search_address) {
        List<Place.Field> fields = Arrays.asList(Place.Field.ADDRESS, Place.Field.LAT_LNG);
        Intent intent = new Autocomplete.IntentBuilder(AutocompleteActivityMode.FULLSCREEN, fields)
                .build(this);
        startAutocomplete.launch(intent);
    }
    

通过以上操作,即可解决重复调用AutocompleteActivity时出现的通道未关闭错误日志,同时保证功能正常运行。

内容的提问来源于stack exchange,提问作者Leonardo Furtado de Luna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 22:45:55