Reputation: 13
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).
Upvotes: 1
Views: 2404
Reputation: 37650
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.
Upvotes: 4