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 afterasync { ... }
defeats the purpose ofasync
. The goal of usingasync
is to run whatever is inside the block concurrently with what is outside (after) the block, until youawait()
it. When youawait()
immediately, it's as if you simply called the code that's inside directly. So you can removeasync
,await
andcoroutineScope
, which you don't seem to need.there is no need for
var x = null
followed byx = something
- just initialize the variable right away. Also, if it won't be changed later, you can useval
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.