Reputation: 2795
Everything works fine, for instance, on Samsung (Android 11), Huawei (Android 10), with the exception of at least Google Pixel 2 (Android 11), Google Pixel 5 (Android 11). There is also no problem with Wi-Fi on these devices.
There is a registration screen. The user enters the data and clicks on the "sign up" button.
Everything is fine until the user performs the following actions:
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 exception is expected)However, it works:
Enable the mobile network
-> Disable the mobile network
-> Click on the "sign up" button
-> For example, the message "email is already in use"
(everything is fine because the suspended coroutine has woken up)Bottom line: Firebase does not throw a FirebaseNetwork or any exception and, as a result, the user interface "freezes" (I disable the form when the request is being processed) when the user submits the form with the mobile network enabled and then submits the form with the mobile network turned off.
private suspend fun handleResult(task: Task<AuthResult>) =
suspendCoroutine<AuthResult> { continuation ->
task.addOnSuccessListener { continuation.resume(it) }
.addOnFailureListener { continuation.resumeWithException(it) }
}
I solved the problem with this answer. The code now looks like:
private suspend fun handleResult(task: Task<AuthResult>) =
withTimeout(5000L) {
suspendCancellableCoroutine<AuthResult> { continuation ->
task.addOnSuccessListener { continuation.resume(it) }
.addOnFailureListener { continuation.resumeWithException(it); }
}
}
Do I need to use the suspendCancellableCoroutine with timeout instead of suspendCoroutine always with Firebase to avoid these bugs on another devices?
Upvotes: 0
Views: 201
Reputation: 2795
The problem is solved by combining await() (thank you, @Tenfour04) + withTimeout(). It looks like Firebase doesn't have a timeout for network authentication calls.
build.gradle to get suspending await() (replace "x.x.x" with the latest version):
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:x.x.x'
For example:
private val firebaseAuth: FirebaseAuth
suspend fun create(userInitial: UserInitial): AuthResult = withTimeout(5000L) {
firebaseAuth.createUserWithEmailAndPassword(
userInitial.email,
userInitial.password
)
.await()
}
withTimeout() throws a TimeoutCancellationException if the timeout was exceeded
Upvotes: 0
Reputation: 93902
I don't know if this is the cause, but it might help. There is already a suspend function that handles Google Tasks without you having to implement suspendCancellableCoroutine
yourself. Their implementation is quite a bit more thorough than yours (it's about 30 lines of code) and maybe handles some edge cases that yours doesn't. It also optimizes results when the task is already finished by the time you call it, and it handles cancellation correctly, which yours does not.
The function is Task.await()
. If it's not available to you, then add this dependency in your build.gradle:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0"
Upvotes: 1