Hi I'm using a 3rd-party library which processes i...
# coroutines
l
Hi I'm using a 3rd-party library which processes images asynchronously (Firebase ML kit for text recognition). I store captured images in a list and when I finished capturing I iterate over the list and process it with the text recognition API. The problem I'm facing is that I want to invoke a callback after all images are processed, but I don't know how. Here is my code. Cam someone have a look please?
h
you must call your
onFinish(result)
lambda inside some callback.
If you wish to invoke
onFinish()
when your task is success you must call it inside
addOnSuccessListener
but if you want to call it when complete you can call it inside
addOnCompleteListener
Bonus: Avoid callbacks You can use
suspendCoroutine()
to make the bridge between callbacks and coroutines. Your code will look better
c
Make the function a
suspend
function, run each image job in an
async
block and
await()
the completion of all tasks. To tie the callback-based image-processing API into the suspending coroutines world, use
suspendCancellableCoroutine { }
Something like the following should get you started:
Copy code
suspend fun startProcessing(taskList: List<String>, onFinish: (List<String>) -> Unit) {
    val results: List<String> = taskList
        .map { image ->
            coroutineScope {
                async {
                    suspendCancellableCoroutine<String> { continuation ->
                        processImage(image) // runs asynchronous
                            .addOnSuccessListener { text -> continuation.resume(text) }
                            .addOnFailureListener { e -> continuation.resumeWithException(e) }
                    }
                }
            }
        }
        .map {
            it.await()
        }

    onFinish(results)
}
l
Thanks guys. @henrikhorbovyi Yeah I know, the problem is the batch processing. onFinish should only be called when all images are processed. @Casey Brooks nice, exactly what I was looking for. But I get following error:
java.lang.ClassCastException: kotlinx.coroutines.CompletedWithCancellation cannot be cast to java.lang.String
thats points to it.await().
it
is Deferred<String> Sorry, never used these classes
ok got it 🙂
h
Oh! Good @Casey Brooks got it better 😄
l
Anyway, thanks for investigation 🙂
c
My example is mostly a pseudocode of it. I’m not sure of the exact types you’re expecting. The type parameter of
suspendCancellableCoroutine<>
should be the type that is emitted on success. That same value is then unwrapped with
it.await()
, so you’ll get a list of the type parameter of
suspendCancellableCoroutine<>
Make sure you’re not calling
.await()
in the first
map { }
. You want to make sure to start all the jobs running in parallel, and then await them all after they’ve all started