简体   繁体   中英

Firebase Auth + Pixel Mobile Network + Suspend Coroutine = Bug?

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:

  1. 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:

  1. 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?

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"

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 "xxx" 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM