Compose Multiplatform实现Google登录遇生命周期错误及方案问询
Compose Multiplatform Google登录问题解决与优化方案
一、解决Android端注册时机错误问题
错误java.lang.IllegalStateException: LifecycleOwner ... is attempting to register while current state is RESUMED的核心原因是registerForActivityResult必须在Activity进入STARTED状态前完成注册,不能在点击事件等晚时机触发注册。结合expect/actual机制,调整方案如下:
1. 共享层定义expect接口
expect class GoogleAuthManager { suspend fun signIn(): GoogleSignInAccount? } // 共享层通用数据类,封装登录结果 data class GoogleSignInAccount(val serverAuthCode: String?)
2. Android端actual实现:提前注册Launcher
在Activity初始化阶段就完成ActivityResultLauncher的注册,而非点击时动态注册:
actual class GoogleAuthManager(private val activity: ComponentActivity) { private lateinit var signInLauncher: ActivityResultLauncher<Intent> private val signInResult = CompletableDeferred<GoogleSignInAccount?>() init { // 在类初始化时注册,确保早于Activity STARTED状态 signInLauncher = activity.registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == Activity.RESULT_OK) { val task = GoogleSignIn.getSignedInAccountFromIntent(result.data) try { val account = task.getResult(ApiException::class.java) signInResult.complete(account?.let { GoogleSignInAccount(it.serverAuthCode) }) } catch (e: ApiException) { signInResult.complete(null) } } else { signInResult.complete(null) } } } actual suspend fun signIn(): GoogleSignInAccount? { val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestServerAuthCode("你的Web端客户端ID") .build() val signInIntent = GoogleSignIn.getClient(activity, signInOptions).signInIntent signInLauncher.launch(signInIntent) return signInResult.await() } }
3. Activity中初始化Manager并传入Compose
class MainActivity : ComponentActivity() { private val googleAuthManager by lazy { GoogleAuthManager(this) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { YourAppTheme { LoginScreen(googleAuthManager) } } } }
4. Compose UI中调用登录
@Composable fun LoginScreen(googleAuthManager: GoogleAuthManager) { var serverAuthCode by remember { mutableStateOf<String?>(null) } Button( onClick = { LaunchedEffect(Unit) { val account = googleAuthManager.signIn() serverAuthCode = account?.serverAuthCode // 后续处理serverAuthCode } } ) { Text("Google 登录") } }
二、跨平台原生Google登录优化方案
如果不想用WebView,expect/actual是官方推荐的原生跨平台实现方式,可进一步优化架构:
1. 封装共享层Repository
将登录逻辑统一到共享层,隔离平台细节:
class AuthRepository(private val googleAuthManager: GoogleAuthManager) { suspend fun signInWithGoogle(): String? { val account = googleAuthManager.signIn() return account?.serverAuthCode } }
2. iOS端actual实现
对应iOS原生GIDSignIn逻辑,获取serverAuthCode:
actual class GoogleAuthManager { actual suspend fun signIn(): GoogleSignInAccount? { return suspendCancellableCoroutine { cont -> val rootVC = UIApplication.shared.keyWindow?.rootViewController GIDSignIn.sharedInstance.signIn(withPresenting: rootVC) { result, error -> if (error != null || result?.user == null) { cont.resume(null) return@signIn } val serverAuthCode = result.user.serverAuthCode cont.resume(GoogleSignInAccount(serverAuthCode)) } } } }
3. 关键注意事项
- Android端:确保
GoogleAuthManager在Activity onCreate阶段初始化,避免Launcher注册时机过晚。 - iOS端:需在
Info.plist配置Google登录的URL Scheme(格式为com.googleusercontent.apps.你的客户端ID),并添加GoogleService-Info.plist文件。 - 共享层仅暴露通用数据类和接口,平台实现细节完全隔离,便于维护。
内容的提问来源于stack exchange,提问作者Nathanael Heitz
相关产品推荐
相关产品推荐

