Android CredentialManager弹窗点击外部关闭无回调问题求助
问题描述
使用Java开发安卓游戏,通过CredentialManager唤起Google账号选择弹窗时出现异常行为:
- 点击弹窗内的「取消」按钮,会正常触发
onError回调并抛出GetCredentialCancellationException - 点击弹窗外部关闭弹窗时,
CancellationSignal.OnCancelListener、onResult、onError均无任何回调触发 - 该问题仅在**三星Galaxy M53 5G(Android 14,One UI 6.0)**上出现,**三星Galaxy Tab A(2018,Android 10,One UI 2.1)**可正常触发
GetCredentialCancellationException
相关代码
登录逻辑代码
public void signIn_credentialManager() { String nonce = randomNonce(); GetCredentialRequest request; googleIdOption = new GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(false) .setServerClientId("...") .setAutoSelectEnabled(true) .setNonce(nonce) .build(); request = new GetCredentialRequest.Builder() .addCredentialOption(googleIdOption) .build(); CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() { @Override public void onCancel() { // 此回调从未触发 log(LOG_TAG, "credentialManager.getCredentialAsync onCancel"); } }); cancellationSignal.throwIfCanceled(); log(LOG_TAG, "credentialManager.getCredentialAsync"); credentialManager.getCredentialAsync( context, request, cancellationSignal, executor, new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() { @Override public void onResult(GetCredentialResponse result) { log(LOG_TAG, "CredentialManagerCallback onResult"); try { ... } catch (Exception e) { log(LOG_TAG, "CredentialManagerCallback Exception!"); e.printStackTrace(); } } @Override public void onError(@NonNull GetCredentialException e) { log(LOG_TAG, "CredentialManagerCallback Error!"); e.printStackTrace(); } } ); }
Android模块build.gradle配置
plugins { id "org.jetbrains.kotlin.android" version "1.9.24" apply false } apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' android { compileSdk 35 sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs('src/main/java') aidl.srcDirs('src/main/java') renderscript.srcDirs('src/main/java') res.srcDirs('res') assets.srcDirs('../assets') jniLibs.srcDirs('libs') } } packagingOptions { resources.with { excludes += ['META-INF/robovm/ios/robovm.xml', 'META-INF/DEPENDENCIES.txt', 'META-INF/DEPENDENCIES', 'META-INF/dependencies.txt', '**/*.gwt.xml'] pickFirsts += ['META-INF/LICENSE.txt', 'META-INF/LICENSE', 'META-INF/license.txt', 'META-INF/LGPL2.1', 'META-INF/NOTICE.txt', 'META-INF/NOTICE', 'META-INF/notice.txt'] } } defaultConfig { applicationId 'app.mywebsite.myappname' minSdkVersion 24 targetSdkVersion 35 versionCode 1 versionName "1.0.0 test" // multiDexEnabled true } namespace "app.mywebsite.myappname" compileOptions { sourceCompatibility "11" targetCompatibility "11" coreLibraryDesugaringEnabled true } buildTypes { debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' debuggable true } release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' debuggable false } } buildFeatures { buildConfig = true } } repositories { google() } configurations { natives } dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" implementation project(':core') natives "com.badlogicgames.gdx:..." implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation platform('com.google.firebase:firebase-bom:33.10.0') implementation 'com.google.firebase:firebase-crashlytics:19.4.1' implementation 'com.google.firebase:firebase-analytics:22.3.0' implementation "androidx.credentials:credentials:1.5.0" implementation "androidx.credentials:credentials-play-services-auth:1.5.0" implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1" implementation "com.google.android.gms:play-services-games-v2:20.1.2" implementation 'com.google.android.gms:play-services-auth:21.3.0' } tasks.register('run', Exec) { def path def localProperties = project.file("../local.properties") if (localProperties.exists()) { Properties properties = new Properties() localProperties.withInputStream { instr -> properties.load(instr) } def sdkDir = properties.getProperty('sdk.dir') if (sdkDir) { path = sdkDir } else { path = "$System.env.ANDROID_SDK_ROOT" } } else { path = "$System.env.ANDROID_SDK_ROOT" } def adb = path + "/platform-tools/adb" commandLine "$adb", 'shell', 'am', 'start', '-n', 'app.mywebsite.myappname/app.mywebsite.myappname.android.MainActivity' } eclipse.project.name = appName + "-android"
核心疑问
CancellationSignal.onCancel是否仅在主动调用.cancel()时才会触发?- 如何处理弹窗外部点击关闭的情况?
解决方案
关于CancellationSignal的说明
CancellationSignal.OnCancelListener确实仅在主动调用cancellationSignal.cancel()时触发,系统弹窗的外部点击关闭并不会自动触发这个信号,这是设计预期。
处理外部点击关闭的方案
方案1:升级Credentials库版本
优先升级androidx.credentials相关依赖到最新稳定版,Google可能已经修复了高版本系统上的回调不一致问题:
implementation "androidx.credentials:credentials:1.6.0" implementation "androidx.credentials:credentials-play-services-auth:1.6.0" implementation "com.google.android.libraries.identity.googleid:googleid:1.2.0"
方案2:监听页面生命周期回调
如果升级库后问题仍存在,可通过监听当前页面的生命周期判断弹窗是否被外部关闭:
- 新增标记位记录弹窗状态:
private boolean isCredentialPickerShowing = false;
- 调用
getCredentialAsync时设置标记:
isCredentialPickerShowing = true; credentialManager.getCredentialAsync(...);
- 在Activity的
onResume中检查并处理:
@Override protected void onResume() { super.onResume(); if (isCredentialPickerShowing) { // 弹窗已关闭且未触发回调,执行取消逻辑 handleCredentialCancellation(); isCredentialPickerShowing = false; } } private void handleCredentialCancellation() { log(LOG_TAG, "Credential picker closed via outside tap"); // 这里添加取消后的处理逻辑,比如重置状态、提示用户等 }
注意:需要在onResult和onError回调中也重置isCredentialPickerShowing = false;,避免误判。
方案3:自定义弹窗拦截(不推荐)
不建议通过自定义弹窗或拦截触摸事件的方式处理,会破坏系统原生的账号选择体验,优先使用前两种方案。
内容的提问来源于stack exchange,提问作者user2342558
相关产品推荐
相关产品推荐

