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

如何在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邮件数据,需要:

  1. 在Google Cloud Console中启用Gmail API
  2. 扩展登录授权范围,添加Gmail相关的权限(比如https://www.googleapis.com/auth/gmail.readonly用于只读邮件)
  3. 使用Google API Client库,通过已认证的用户凭据调用Gmail API接口

注意:Firebase认证仅完成了用户身份验证,要访问Gmail数据需要额外的API授权,需确保在登录请求中添加对应的scope。

内容的提问来源于stack exchange,提问作者Robby

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:47:34