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

