Firebase Auth + Pixel Mobile Network + Suspend Coroutine = Bug?
Firebase Auth + Pixel Mobile Network + Suspend Coroutine = Bug?
一切正常,例如,三星 (Android 11)、华为 (Android 10),除了至少 Google Pixel 2 (Android 11),Google Pixel 5 (Android 11)。 这些设备上的 Wi-Fi 也没有问题。
有一个注册屏幕。用户输入数据并点击“注册”按钮。
一切正常,直到用户执行以下操作:
Enable the mobile network
-> Click on the "sign up" button
-> For example, the message "email is already in use"
-> Disable the mobile network
-> Click on the "sign up" button
-> The suspended coroutine never continues
(FirebaseNetwork 异常是预期的)
但是,它有效:
Enable the mobile network
-> Disable the mobile network
-> Click on the "sign up" button
-> For example, the message "email is already in use"
(一切正常因为挂起的协程已经唤醒)
底线:Firebase 不会抛出 FirebaseNetwork 或任何异常,因此用户界面“冻结”(我在处理请求时禁用了表单) 当用户在启用移动网络的情况下提交表单,然后在关闭移动网络的情况下提交表单。
private suspend fun handleResult(task: Task<AuthResult>) =
suspendCoroutine<AuthResult> { continuation ->
task.addOnSuccessListener { continuation.resume(it) }
.addOnFailureListener { continuation.resumeWithException(it) }
}
我用 答案解决了问题。
代码现在看起来像:
private suspend fun handleResult(task: Task<AuthResult>) =
withTimeout(5000L) {
suspendCancellableCoroutine<AuthResult> { continuation ->
task.addOnSuccessListener { continuation.resume(it) }
.addOnFailureListener { continuation.resumeWithException(it); }
}
}
我是否需要使用带超时的 suspendCancellableCoroutine 而不是始终与 Firebase 一起使用 suspendCoroutine 以避免在其他设备上出现这些错误?
我不知道这是否是原因,但这可能会有所帮助。已经有一个挂起函数可以处理 Google 任务,而无需您自己实现 suspendCancellableCoroutine
。他们的实现比你的更彻底(大约 30 行代码)并且可能处理一些你没有的边缘情况。当您调用它时任务已经完成时,它还会优化结果,并且它会正确处理取消,而您的则不会。
函数为Task.await()
。如果它对您不可用,请将此依赖项添加到您的 build.gradle:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0"
结合await()(谢谢@Tenfour04)+withTimeout()解决了这个问题。 Firebase 似乎没有网络身份验证调用超时。
build.gradle 暂停 await()(将“x.x.x”替换为最新版本):
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:x.x.x'
例如:
private val firebaseAuth: FirebaseAuth
suspend fun create(userInitial: UserInitial): AuthResult = withTimeout(5000L) {
firebaseAuth.createUserWithEmailAndPassword(
userInitial.email,
userInitial.password
)
.await()
}
withTimeout() 如果超过超时则抛出 TimeoutCancellationException
一切正常,例如,三星 (Android 11)、华为 (Android 10),除了至少 Google Pixel 2 (Android 11),Google Pixel 5 (Android 11)。 这些设备上的 Wi-Fi 也没有问题。
有一个注册屏幕。用户输入数据并点击“注册”按钮。
一切正常,直到用户执行以下操作:
Enable the mobile network
->Click on the "sign up" button
->For example, the message "email is already in use"
->Disable the mobile network
->Click on the "sign up" button
->The suspended coroutine never continues
(FirebaseNetwork 异常是预期的)
但是,它有效:
Enable the mobile network
->Disable the mobile network
->Click on the "sign up" button
->For example, the message "email is already in use"
(一切正常因为挂起的协程已经唤醒)
底线:Firebase 不会抛出 FirebaseNetwork 或任何异常,因此用户界面“冻结”(我在处理请求时禁用了表单) 当用户在启用移动网络的情况下提交表单,然后在关闭移动网络的情况下提交表单。
private suspend fun handleResult(task: Task<AuthResult>) =
suspendCoroutine<AuthResult> { continuation ->
task.addOnSuccessListener { continuation.resume(it) }
.addOnFailureListener { continuation.resumeWithException(it) }
}
我用
private suspend fun handleResult(task: Task<AuthResult>) =
withTimeout(5000L) {
suspendCancellableCoroutine<AuthResult> { continuation ->
task.addOnSuccessListener { continuation.resume(it) }
.addOnFailureListener { continuation.resumeWithException(it); }
}
}
我是否需要使用带超时的 suspendCancellableCoroutine 而不是始终与 Firebase 一起使用 suspendCoroutine 以避免在其他设备上出现这些错误?
我不知道这是否是原因,但这可能会有所帮助。已经有一个挂起函数可以处理 Google 任务,而无需您自己实现 suspendCancellableCoroutine
。他们的实现比你的更彻底(大约 30 行代码)并且可能处理一些你没有的边缘情况。当您调用它时任务已经完成时,它还会优化结果,并且它会正确处理取消,而您的则不会。
函数为Task.await()
。如果它对您不可用,请将此依赖项添加到您的 build.gradle:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0"
结合await()(谢谢@Tenfour04)+withTimeout()解决了这个问题。 Firebase 似乎没有网络身份验证调用超时。
build.gradle 暂停 await()(将“x.x.x”替换为最新版本):
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:x.x.x'
例如:
private val firebaseAuth: FirebaseAuth
suspend fun create(userInitial: UserInitial): AuthResult = withTimeout(5000L) {
firebaseAuth.createUserWithEmailAndPassword(
userInitial.email,
userInitial.password
)
.await()
}
withTimeout() 如果超过超时则抛出 TimeoutCancellationException