rememberLauncherForActivityResult的onResult未触发问题排查
问题描述
实现Google一键登录时,rememberLauncherForActivityResult的onResult回调始终无法触发,但重写宿主Activity的onActivityResult方法可以正常接收登录结果。设置noHistory标志也未产生任何效果。
启动Launcher的代码:
var authStarted by remember { mutableStateOf(true) } val activity = LocalContext.current.findActivity() val launcher = rememberLauncherForActivityResult( contract = GoogleSignInContract(), onResult = { it -> if (it == null) return@rememberLauncherForActivityResult it.addOnSuccessListener { if (it.idToken == null) return@addOnSuccessListener FBAuth.firebaseAuthWithGoogle(activity, it.idToken!!) } }) if (authStarted) { SideEffect { launcher.launch(AUTH_CODE) authStarted = false } }
自定义Contract代码:
class GoogleSignInContract : ActivityResultContract<Int, Task<GoogleSignInAccount>?>() { override fun createIntent(context: Context, input: Int): Intent { val client = GoogleSignIn.getClient(context, FBAuth.newGsi(context.getString(R.string.web_oauth))) return client.signInIntent } override fun parseResult(resultCode: Int, intent: Intent?): Task<GoogleSignInAccount>? { Log.d("GoogleSignInContract", "Result code: $resultCode") return when (resultCode) { Activity.RESULT_OK -> { GoogleSignIn.getSignedInAccountFromIntent(intent) } else -> { null } } } }
解决方案
检查宿主Activity的onActivityResult实现
如果宿主Activity重写了onActivityResult方法,必须确保调用了super.onActivityResult(requestCode, resultCode, data)——否则Jetpack Compose的ActivityResultLauncher无法接收结果回调,这是最常见的原因。替换自定义Contract为官方实现
自定义ActivityResultContract容易引入潜在问题,直接使用官方的ActivityResultContracts.StartActivityForResult()可以简化逻辑并避免回调丢失:val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { val signInTask = GoogleSignIn.getSignedInAccountFromIntent(result.data) signInTask.addOnSuccessListener { account -> account.idToken?.let { token -> FBAuth.firebaseAuthWithGoogle(activity, token) } } signInTask.addOnFailureListener { // 处理登录失败场景,比如用户取消登录 } } } // 启动登录流程 LaunchedEffect(Unit) { val signInClient = GoogleSignIn.getClient(activity, FBAuth.newGsi(activity.getString(R.string.web_oauth))) launcher.launch(signInClient.signInIntent) }改用LaunchedEffect替代SideEffect
SideEffect会在每次重组时执行,虽然你设置了authStarted = false,但LaunchedEffect(Unit)更适合执行一次性的启动操作,确保Launcher只被调用一次,避免因重组导致的异常。检查Activity启动模式
避免为宿主Activity设置singleInstance或singleTask启动模式,这类模式会改变Activity栈的行为,导致结果无法正确回调到Compose的Launcher。保持默认的standard启动模式即可。验证上下文获取的正确性
可以直接使用LocalActivity.current(需导入androidx.activity.compose.LocalActivity)替代自定义的findActivity()方法,确保Launcher绑定到当前正确的宿主Activity,避免因上下文错误导致的回调丢失。
内容的提问来源于stack exchange,提问作者AdisAlagic

