KMP 中 Firebase Auth 的 await 和 user 为何无法解析?
需要修正两处:先对 signInWithEmailAndPassword() 返回的 Task 调用 .await(),再读取 .user;同时确保 kotlinx-coroutines-play-services 已进入 :data 模块的 Android 编译依赖,并导入 kotlinx.coroutines.tasks.await。
原代码把 Task 当成了登录结果,存在类型使用错误。至于导入为何不可用,单凭当前片段还无法确定,需要检查依赖解析和编译日志。
为什么 .user 无法解析?
signInWithEmailAndPassword() 返回的是 Task<AuthResult>。原代码中的变量虽然叫 authResult,实际类型仍是 Task,因此不能直接读取 AuthResult.user。Firebase 的返回类型见 FirebaseAuth API 文档。
应改为:
val authResult = auth.signInWithEmailAndPassword(email, password).await()
val user = authResult.user
这里的 .await() 等待任务完成并返回其中的 AuthResult,等待期间不会阻塞线程。该扩展函数由 kotlinx-coroutines-play-services 提供,位于 kotlinx.coroutines.tasks 包中,详见 Task.await API 文档。
如何配置 androidMain 的依赖?
题目将依赖声明在 androidMain 中的方向是正确的。Android 专用依赖应声明在使用它的 KMP 模块的对应源集中,参见 KMP Android 依赖配置说明。
下面保留题目中的依赖版本,说明配置位置;使用前仍需确认它们与实际 Kotlin 编译器兼容:
kotlin {
androidTarget()
sourceSets {
val androidMain by getting {
dependencies {
implementation(
project.dependencies.platform(
"com.google.firebase:firebase-bom:33.10.0"
)
)
implementation("com.google.firebase:firebase-auth")
implementation(
"org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.2"
)
}
}
}
}
project.dependencies.platform(...) 显式使用 Gradle 的 platform API。如果原来的 platform(...) 已能正常解析,无须仅为解决这个错误改写它。
随后在 FirebaseAuthDataSource.kt 中添加:
import com.google.firebase.auth.FirebaseAuth
import kotlinx.coroutines.tasks.await
kotlinx-coroutines-android 提供 Android 协程支持,但不能代替 Firebase Task 所需的 kotlinx-coroutines-play-services。后者提供 Task.await(),参见 协程 Play Services 集成模块。Firebase BoM 也不会管理 org.jetbrains.kotlinx 的协程版本。
完整的登录实现
以下代码沿用项目已有的 AuthRemoteRepository 和 UserToken 定义:
import com.google.firebase.auth.FirebaseAuth
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.tasks.await
class FirebaseAuthDataSource(
private val auth: FirebaseAuth
) : AuthRemoteRepository {
override suspend fun login(
email: String,
password: String
): Result<UserToken> {
return try {
val authResult = auth
.signInWithEmailAndPassword(email, password)
.await()
val user = authResult.user
?: throw IllegalStateException("User is null")
val tokenResult = user.getIdToken(true).await()
val token = tokenResult.token
?: throw IllegalStateException("ID token is null")
Result.success(UserToken(token))
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
Result.failure(e)
}
}
}
两次 .await() 分别取得登录结果和 ID token 结果。示例在 token 为 null 时返回失败,避免通过 Result.success(UserToken("")) 返回缺失的凭据。
await() 支持协程取消,因此应先重新抛出 CancellationException,避免它被通用的 catch (e: Exception) 转换为普通业务失败。
导入仍然不存在时,按什么顺序排查?
- 确认文件属于正确源集。实现文件应位于
:data模块的src/androidMain/kotlin/下。仅放在其他模块的androidMain,或实际位于commonMain,都不能据此获得当前模块的 Android 专用依赖。 - 检查 Gradle Sync 的首个错误。确认依赖仓库包含
google()和mavenCentral(),并查看是否存在下载失败、仓库访问失败或依赖解析失败。 - 确认依赖进入 Android 编译配置。在项目根目录执行下面的命令,检查 Android 编译相关配置中是否包含
kotlinx-coroutines-play-services,以及最终解析到的版本:
./gradlew :data:dependencies
- 核对实际 Kotlin 版本。题目中的
2.0.0(或实际版本)不能作为最终版本依据,应检查项目插件配置或版本目录。kotlinx.coroutines从 1.10.0 起将构建所用 Kotlin 升级到 2.1.0,见 1.10.0 发布说明。如果日志出现 Kotlin metadata 版本不兼容,应据此调整 Kotlin 工具链或选择兼容的协程版本,并检查其他协程模块是否被依赖解析提升了版本。不能只凭Unresolved reference就认定一定是版本冲突。 - 依赖和编译恢复正常后,再处理 IDE 索引。若命令行编译正常而 IDE 仍标红,再重新同步或重建索引。依赖尚未解析成功时,反复清缓存通常无法解决根因。
不需要为解决这个错误把 Android Firebase SDK 移入 commonMain。共享接口可以保留在公共代码中,Firebase 实现继续放在 androidMain。
备注:内容仅供参考。
