如何在Jetpack Compose中正确配置Google一键登录并解决认证问题
问题分析与修正方案
你的代码存在几个关键问题导致认证环节卡住,以下是针对性的修正和说明:
1. 重复的认证逻辑(核心问题)
你在onActivityResult中写了两段完全重复的Google凭证解析和Firebase认证代码,第一段在REQ_ONE_TAP的when分支内,第二段又在分支外执行,这会导致重复调用认证接口,甚至在分支内捕获异常后,分支外的代码仍会执行引发空指针。
2. 缺失TAG常量
代码中使用Log.d(TAG, ...)但未定义TAG常量,会直接编译失败。
3. 首次登录无已授权账号时OneTap失败
setFilterByAuthorizedAccounts(true)会只显示已登录过的账号,首次登录时没有已授权账号,OneTap会直接进入失败回调,无法触发登录UI。
4. Composable中跳转上下文错误
MainActivity的SignInButton里,跳转Intent使用的this是Composable的lambda上下文,并非正确的Activity上下文,会导致跳转异常。
修正后的完整代码
GSignIn.kt
class GSignIn : AppCompatActivity() { // 补充TAG常量 private val TAG = "GSignIn" private lateinit var oneTapClient: SignInClient private lateinit var signInRequest: BeginSignInRequest private val REQ_ONE_TAP = 2 private var showOneTapUI = true private lateinit var auth: FirebaseAuth override fun onStart() { super.onStart() val currentUser = auth.currentUser // 如果已登录,可直接跳转至邮件页面 currentUser?.let { Log.d(TAG, "用户已登录:${it.displayName}") finish() // 关闭当前登录页 } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { REQ_ONE_TAP -> { try { val credential = oneTapClient.getSignInCredentialFromIntent(data) val idToken = credential.googleIdToken idToken?.let { Log.d(TAG, "获取到ID Token") // 仅在这里执行Firebase认证,避免重复 val firebaseCredential = GoogleAuthProvider.getCredential(it, null) auth.signInWithCredential(firebaseCredential) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { Log.d(TAG, "Firebase认证成功") val user = auth.currentUser user?.let { Log.d(TAG, "当前用户:${it.email}") // 认证成功后跳转至邮件列表页面 finish() } } else { Log.w(TAG, "Firebase认证失败", task.exception) } } } ?: Log.d(TAG, "未获取到ID Token") } catch (e: ApiException) { Log.e(TAG, "认证异常:${e.localizedMessage}", e) } } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 给Activity设置一个空白布局(避免异常,也可添加加载提示) setContentView(R.layout.activity_g_sign_in) // 初始化Firebase Auth auth = Firebase.auth oneTapClient = Identity.getSignInClient(this) signInRequest = BeginSignInRequest.builder() .setGoogleIdTokenRequestOptions( BeginSignInRequest.GoogleIdTokenRequestOptions.builder() .setSupported(true) .setServerClientId(getString(R.string.your_web_client_id)) // 改为false,允许首次登录选择新账号 .setFilterByAuthorizedAccounts(false) .build() ) // 允许用户创建新账号(可选) .setAutoSelectEnabled(true) .build() // 启动One Tap登录 oneTapClient.beginSignIn(signInRequest) .addOnSuccessListener(this) { result -> try { startIntentSenderForResult( result.pendingIntent.intentSender, REQ_ONE_TAP, null, 0, 0, 0, null ) } catch (e: IntentSender.SendIntentException) { Log.e(TAG, "无法启动One Tap UI:${e.localizedMessage}") } } .addOnFailureListener(this) { e -> Log.d(TAG, "One Tap启动失败:${e.localizedMessage}") // 失败时可 fallback 到普通Google登录按钮 } } }
MainActivity.kt
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Aiko_mailTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { // 使用LocalContext获取正确的上下文 val context = LocalContext.current SignInButton(onClick = { val intent = Intent(context, GSignIn::class.java) context.startActivity(intent) }) } } } } } @Composable fun SignInButton(onClick: () -> Unit) { Button( onClick = onClick, modifier = Modifier.fillMaxWidth().padding(16.dp), ) { Text(text = "登录Gmail") } }
后续获取Gmail数据的思路
认证成功后,若要获取Gmail邮件数据,需要:
- 在Google Cloud Console中启用Gmail API
- 扩展登录授权范围,添加Gmail相关的权限(比如
https://www.googleapis.com/auth/gmail.readonly用于只读邮件) - 使用Google API Client库,通过已认证的用户凭据调用Gmail API接口
注意:Firebase认证仅完成了用户身份验证,要访问Gmail数据需要额外的API授权,需确保在登录请求中添加对应的scope。
内容的提问来源于stack exchange,提问作者Robby
相关产品推荐
相关产品推荐

