Google Credential Manager首次登录报“No credentials available”问题排查
新版Credential Manager API Google登录首次失败问题
问题现象
使用新版Credential Manager API实现Google登录时,应用安装后首次登录尝试始终失败,报错No credentials available,但多次尝试或等待一段时间后可正常完成登录。
相关实现代码
GetGoogleCredentialUseCase
class GetGoogleCredentialUseCase @Inject constructor( @ApplicationContext private val context: Context ) { suspend fun getGoogleCredential(): GetCredentialResponse = withContext(Dispatchers.IO) { Log.d("LoginDebug", "Starting Google login attempt") val auth = FirebaseAuth.getInstance() Log.d("LoginDebug", "Firebase login status before credential request: ${auth.currentUser != null}") val credentialManager = createCredentialManager() Log.d("LoginDebug", "createCredentialManager completed, ${credentialManager}") val request = createCredentialRequest() Log.d("LoginDebug", "createCredentialRequest completed, ${request}") fetchCredential(credentialManager, request) } private fun createCredentialManager(): CredentialManager { try { val manager = CredentialManager.create(context) Log.d("CredentialDebug", "CredentialManager creation successful") return manager } catch (e: Exception) { Log.e("CredentialDebug", "CredentialManager creation failed: ${e.javaClass.simpleName} - ${e.message}", e) throw e } } private fun createCredentialRequest(): GetCredentialRequest { val clientId = BuildConfig.GOOGLE_CLIENT_ID Log.d("CredentialDebug", "Using Google client ID: $clientId") try { // Check Google Play Services status val apiAvailability = GoogleApiAvailability.getInstance() val resultCode = apiAvailability.isGooglePlayServicesAvailable(context) Log.d("CredentialDebug", "Google Play Services status: $resultCode (0 is success)") // Check Google account status val accountManager = AccountManager.get(context) val accounts = accountManager.getAccountsByType("com.google") Log.d("CredentialDebug", "Number of Google accounts registered on device: ${accounts.size}") // Check security providers try { val installedProviders = Security.getProviders().map { it.name } Log.d("CredentialDebug", "Installed security providers: $installedProviders") } catch (e: Exception) { Log.w("CredentialDebug", "Failed to retrieve security providers list", e) } val googleIdOption = GetSignInWithGoogleOption.Builder(clientId) .build() Log.d("CredentialDebug", "GetSignInWithGoogleOption creation successful") val request = GetCredentialRequest.Builder() .addCredentialOption(googleIdOption) .build() Log.d("CredentialDebug", "GetCredentialRequest creation successful") return request } catch (e: Exception) { Log.e("CredentialDebug", "Authentication request creation failed: ${e.javaClass.simpleName} - ${e.message}", e) throw e } } @SuppressLint("NewApi") private suspend fun fetchCredential( credentialManager: CredentialManager, request: GetCredentialRequest ): GetCredentialResponse { return try { Log.d("CredentialDebug", "getCredential call started, current thread: ${Thread.currentThread().name}") // Check Firebase authentication status val firebaseAuth = FirebaseAuth.getInstance() Log.d("CredentialDebug", "Firebase login status: ${firebaseAuth.currentUser != null}") // Check system time val currentTime = System.currentTimeMillis() val formattedTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format( Date(currentTime) ) Log.d("CredentialDebug", "Current system time: $formattedTime") withContext(Dispatchers.IO) { try { credentialManager.getCredential(context, request) } catch (e: Exception) { Log.e("CredentialDebug", "Authentication request failed in IO context: ${e.javaClass.simpleName}", e) throw e } } } catch (e: Exception) { Log.e("CredentialDebug", "Authentication request failed: ${e.javaClass.simpleName} - ${e.message}", e) throw e } } }
ViewModel登录逻辑
override fun signInWithGoogle() { viewModelScope.launch { authManager.startLoading() try { // 1. Check initialization appStateHolder.isFirebaseInitialized.first { it } // 2. Allow time for authentication service preparation delay(1500L) // 3. Retry logic with clearCredentials failure handling var attempt = 0 var lastError: Exception? = null while (attempt < 3) { try { if (attempt > 0) { delay(1000L * attempt) Log.d("LoginFlow", "Google login retry ${attempt+1}/3") } // Wrap clearCredentials with try-catch try { authManager.clearCredentials() } catch (e: Exception) { Log.w("LoginFlow", "Credential initialization failed, continuing: ${e.message}") } val result = authManager.getGoogleCredential() processSignInResult(result) break } catch (e: Exception) { val errorMsg = e.message ?: "Unknown error" Log.e("LoginFlow", "Attempt ${attempt+1} failed: $errorMsg", e) lastError = e attempt++ if (!shouldRetry(e) || attempt >= 3) { throw lastError ?: RuntimeException("Authentication failed") } } } } catch (e: Exception) { Log.e("LoginFlow", "signInWithGoogle error emergency!", e) handleException(e) } finally { authManager.stopLoading() } } }
日志信息
失败尝试日志
2025-02-21 01:56:37.709 3811-3811 LoginDebug com.example.myApp D Starting Google login attempt 2025-02-21 01:56:38.426 3811-4822 CredManProvService com.example.myApp I GetCredentialResponse error returned from framework 2025-02-21 01:56:38.427 3811-3811 LoginDebug com.example.myApp D No authorized accounts, retrying with all accounts 2025-02-21 01:56:38.714 3811-4822 CredManProvService com.example.myApp I GetCredentialResponse error returned from framework 2025-02-21 01:56:38.714 3811-3811 LoginDebug com.example.myApp D All GetGoogleIdOption attempts failed, trying GetSignInWithGoogleOption 2025-02-21 01:56:39.720 3811-4065 CredManProvService com.example.myApp I GetCredentialResponse error returned from framework 2025-02-21 01:56:39.726 3811-3811 LoginFlow com.example.myApp E Attempt 1 failed: No credentials available b1.h: No credentials available
成功尝试日志
2025-02-21 01:52:11.656 12221-12300 EGL_emulation com.example.myApp D app_time_stats: avg=396.17ms min=4.37ms max=1641.80ms count=6 2025-02-21 01:52:13.309 12221-12300 EGL_emulation com.example.myApp D app_time_stats: avg=28.43ms min=1.67ms max=959.90ms count=41 2025-02-21 01:52:14.778 12221-12300 EGL_emulation com.example.myApp D app_time_stats: avg=24.68ms min=2.26ms max=780.65ms count=41 2025-02-21 01:52:16.345 12221-12294 CredentialDebug com.example.myApp D CredentialManager creation successful 2025-02-21 01:52:16.356 12221-12294 CredManProvService com.example.myApp I In CredentialProviderFrameworkImpl onClearCredential 2025-02-21 01:52:17.371 12221-12294 FirebaseAuth com.example.myApp D Notifying id token listeners about a sign-out event. 2025-02-21 01:52:17.372 12221-12294 FirebaseAuth com.example.myApp D Notifying auth state listeners about a sign-out event. 2025-02-21 01:52:17.381 12221-12221 LoginDebug com.example.myApp D Starting Google login attempt 2025-02-21 01:52:17.720 12221-12499 CredManProvService com.example.myApp I GetCredentialResponse error returned from framework 2025-02-21 01:52:17.720 12221-12221 LoginDebug com.example.myApp D No authorized accounts, retrying with all accounts 2025-02-21 01:52:17.795 12221-12499 CredManProvService com.example.myApp I GetCredentialResponse error returned from framework 2025-02-21 01:52:17.795 12221-12221 LoginDebug com.example.myApp D All GetGoogleIdOption attempts failed, trying GetSignInWithGoogleOption
关键观察
成功案例中,尝试GetSignInWithGoogleOption后无错误日志;失败案例中则出现明确的No credentials available错误。
已尝试方案
- 使用不同凭证请求策略:
GetGoogleIdOption+setFilterByAuthorizedAccounts(true)、GetGoogleIdOption+setFilterByAuthorizedAccounts(false)、GetSignInWithGoogleOption - 在尝试之间添加延迟
- 尝试前清除凭证
- 确认Google Play Services可用
疑问与解答
1. 为何多次尝试或等待后认证可成功?
首次安装后,Google Play Services相关的认证服务、账户同步或Credential Manager的底层缓存尚未完成初始化。这些后台操作需要时间执行,等待或重试时,系统已经完成了必要的准备工作,因此请求能够成功。
2. Credential Manager是否需要特定初始化操作才能正常工作?
Credential Manager本身通过CredentialManager.create(context)即可完成初始化,但它依赖的Google Play Services组件、系统账户服务需要时间完成启动和同步。首次安装后,这些依赖服务可能还在后台初始化,导致Credential Manager无法获取到可用凭证。
3. 这是否与Google Play Services后台初始化有关?
是的,完全相关。Google Play Services负责处理Google账户的认证、凭证管理等核心逻辑,首次安装应用后,Play Services可能需要完成账户同步、服务注册等异步操作,未完成时Credential Manager的请求就会失败。
4. 在此场景下处理“No credentials available”的最佳实践是什么?
- 智能重试策略:针对该错误实现指数退避重试(比如第一次延迟1s,第二次2s,最多3次),避免固定延迟,减少不必要的等待。
- 前置检查:在发起登录请求前,确保Google Play Services已完全可用(不仅是
isGooglePlayServicesAvailable返回成功,还可监听GoogleApiAvailability的回调或等待账户同步完成)。 - 用户友好提示:首次失败时告知用户“正在准备登录服务,请稍候”,而非直接提示登录失败,提升用户体验。
- 避免频繁清除凭证:
clearCredentials可能干扰Credential Manager的缓存机制,仅在确定有缓存问题时使用,不要每次重试都调用。
内容的提问来源于stack exchange,提问作者Yoon
相关产品推荐
相关产品推荐

