Home > Software design >  Get specific value from Coroutine after the fun is completed
Get specific value from Coroutine after the fun is completed

Time:03-23

I'm a beginner in kotlin and I use coroutine for the first time in android.

I want to get the value of classification after the async call is completed and transfer the result to the fragment, where this function exists in the non-activity class.

I wrote the following function but it return an empty string with the first click of the button and return the value with the second click.

Could you please help me?

The calling in a fragment:

runBlocking{
imgResultTextView.setText(imageClassificationBean.imageClassification())}

The fun in non-activity class:

 suspend fun imageClassification(bitmap: Bitmap?): String = coroutineScope {
     val result = async {
         //ML process (image classification)
         var imageLabeler: ImageLabeler? = null
         imageLabeler = ImageLabeling.getClient(
             ImageLabelerOptions.Builder()
                 .setConfidenceThreshold(0.7f)
                 .build()
         )

         val inputImage = InputImage.fromBitmap(bitmap, 0)
         imageLabeler.process(inputImage)
             .addOnSuccessListener(OnSuccessListener { imageLabels: List<ImageLabel> ->
                 Log.i("lolo", "percentageDeferred: 2 "  imageLabels)
                 val sb = StringBuilder()
                 for (label in imageLabels) {
                     sb.append(label.text).append(": ").append(label.confidence).append("\n")
                handleResult(sb.toString())
                 }
                 if (imageLabels.isEmpty()) {
                     classication = "Could not classify!!"
                 } else {
                     classication = sb.toString()
                 }
             }).addOnFailureListener(OnFailureListener { e: Exception ->
                 e.printStackTrace()
            handleResult("error")
             })
     }
     result.await()
     result.getCompleted()
         
     return@coroutineScope classication
}

I want the result to display in the textview of fragments from the first click of the button (classification value).

CodePudding user response:

The problem here is that you're not waiting for the actual asynchronous operation (which is callback-based). You're wrapping it in an unnecessary async which you then await, but the underlying operation (imageLabeler.process()) is still asynchronous within the async block and nothing waits for it.

Here are several things about your code:

  • using await() right after async { ... } defeats the purpose of async. The goal of using async is to run whatever is inside the block concurrently with what is outside (after) the block, until you await() it. When you await() immediately, it's as if you simply called the code that's inside directly. So you can remove async, await and coroutineScope, which you don't seem to need.

  • there is no need for var x = null followed by x = something - just initialize the variable right away. Also, if it won't be changed later, you can use val instead.

What you probably want instead is to wrap your callback-based API into a suspending function. That you can do with suspendCoroutine or suspendCancellableCoroutine (depending on whether the API you call is cancellable and/or if you want your function to be cancellable anyway).

The doc provides examples on how to do that.

  • Related