如何等待任务完成和 return 变量?
How to await Task completion and return a variable?
我一直在尝试编写“惯用的”Kotlin 异步代码。我正在尝试将条形码扫描仪重构为顶级/包级功能。
如何让线程等待 scanner.process(image)
完成,并在继续之前等待 return 条形码列表?
代码部分显示了我“最接近”解决问题的尝试。
package com.example.demo
import android.util.Log
import com.google.mlkit.vision.barcode.Barcode
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
import kotlinx.coroutines.*
class BarcodeActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val scanButton = findViewById<Button>(...)
scanButton.setOnClickListener {
// Get Bitmap image
runBarcode(image)
}
}
// Member function, calling codes should return a list of barcodes
// after completing
fun runBarcode(image: InputImage) = runBlocking {
Log.d("BAR", "One")
val codes = async { scanBarcodes(image)}
Log.d("BAR", "Three")
}
}
// Package level function
suspend fun scanBarcodes(image: InputImage) = coroutineScope {
val scanner = BarcodeScanning.getClient()
val codes = async {
scanner.process(image)
.addOnSuccessListener { barcodes ->
val barcodeList = mutableListOf<Barcode>()
Log.d("BAR", "Two")
for (barcode in barcodes) {
barcodeList.add(barcode)
}
return@addOnSuccessListener
}
.addOnFailureListener {
Log.e("BAR", "Barcode scan failed")
}
}
codes.await()
}
打印
D/BAR One
D/BAR Three
D/BAR Two
并且 scanBarcodes
的推断 return 类型是 Task<MutableListOf<Barcode>>
在 Dart/Flutter 中,我会写类似 <T> scanBarcodes() async {}
的东西,相应地 var codes = await ...
来解决这个问题。我想我可以在完成时使用 val codes = runBlocking{...}
来阻塞主线程。但是,这种异步模式在 Kotlin 中显然是 strongly discouraged。
您可以将 runBarcode()
重写如下。
fun runBarcode(image: InputImage) {
lifecycle.coroutineScope.launch {
Log.d("BAR", "One")
val codes = async { scanBarcodes(image)}
codes.await()
Log.d("BAR", "Three")
}
}
await() 是可挂起的函数,它将 return DeferedJob 对象,它具有 return 来自可挂起函数 (DefferedJob())(如果有)的结果值。这将帮助您打印
D/BAR One
D/BAR Two
D/BAR Three
希望这就是您所期待的..
不要为此使用 runBlocking
。它会阻塞您的主线程,从而在等待时冻结 UI(用户无法滚动、单击甚至离开您的应用程序)。它还会让您面临 ANR 崩溃的风险。您应该从您的点击侦听器中启动协程,这样您就可以将此函数设为暂停函数。
要将回调用作挂起函数,通常您必须使用 suspendCancellableCoroutine()
将其转换为挂起的东西。但是,在这种情况下,Kotlin 协程库已经为您提供了 Task.await()
挂起函数。如果无法在您的项目中导入,请将此依赖项添加到您的 build.gradle:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0"
await()
函数returns任务的结果,否则任务失败会抛出异常,所以应该用try/catch包起来,而不是用success和failure听众。
修复代码的方法如下:
class BarcodeActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val scanButton = findViewById<Button>(...)
scanButton.setOnClickListener {
lifecycleScope.launch {
val barcodes = scanBarcodes(image)
// do something with returned barcodes or maybe show message if list empty
}
}
}
}
// Package level function
suspend fun scanBarcodes(image: InputImage): List<Barcode> {
return try {
BarcodeScanning.getClient()
.process(image)
.await()
} catch(e: Exception) {
Log.e("BAR", "Barcode scan failed")
emptyList() // Returns an empty list on failure, but you might want to handle it differently, like returning null.
}
}
我一直在尝试编写“惯用的”Kotlin 异步代码。我正在尝试将条形码扫描仪重构为顶级/包级功能。
如何让线程等待 scanner.process(image)
完成,并在继续之前等待 return 条形码列表?
代码部分显示了我“最接近”解决问题的尝试。
package com.example.demo
import android.util.Log
import com.google.mlkit.vision.barcode.Barcode
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
import kotlinx.coroutines.*
class BarcodeActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val scanButton = findViewById<Button>(...)
scanButton.setOnClickListener {
// Get Bitmap image
runBarcode(image)
}
}
// Member function, calling codes should return a list of barcodes
// after completing
fun runBarcode(image: InputImage) = runBlocking {
Log.d("BAR", "One")
val codes = async { scanBarcodes(image)}
Log.d("BAR", "Three")
}
}
// Package level function
suspend fun scanBarcodes(image: InputImage) = coroutineScope {
val scanner = BarcodeScanning.getClient()
val codes = async {
scanner.process(image)
.addOnSuccessListener { barcodes ->
val barcodeList = mutableListOf<Barcode>()
Log.d("BAR", "Two")
for (barcode in barcodes) {
barcodeList.add(barcode)
}
return@addOnSuccessListener
}
.addOnFailureListener {
Log.e("BAR", "Barcode scan failed")
}
}
codes.await()
}
打印
D/BAR One
D/BAR Three
D/BAR Two
并且 scanBarcodes
的推断 return 类型是 Task<MutableListOf<Barcode>>
在 Dart/Flutter 中,我会写类似 <T> scanBarcodes() async {}
的东西,相应地 var codes = await ...
来解决这个问题。我想我可以在完成时使用 val codes = runBlocking{...}
来阻塞主线程。但是,这种异步模式在 Kotlin 中显然是 strongly discouraged。
您可以将 runBarcode()
重写如下。
fun runBarcode(image: InputImage) {
lifecycle.coroutineScope.launch {
Log.d("BAR", "One")
val codes = async { scanBarcodes(image)}
codes.await()
Log.d("BAR", "Three")
}
}
await() 是可挂起的函数,它将 return DeferedJob 对象,它具有 return 来自可挂起函数 (DefferedJob())(如果有)的结果值。这将帮助您打印
D/BAR One
D/BAR Two
D/BAR Three
希望这就是您所期待的..
不要为此使用
runBlocking
。它会阻塞您的主线程,从而在等待时冻结 UI(用户无法滚动、单击甚至离开您的应用程序)。它还会让您面临 ANR 崩溃的风险。您应该从您的点击侦听器中启动协程,这样您就可以将此函数设为暂停函数。要将回调用作挂起函数,通常您必须使用
suspendCancellableCoroutine()
将其转换为挂起的东西。但是,在这种情况下,Kotlin 协程库已经为您提供了Task.await()
挂起函数。如果无法在您的项目中导入,请将此依赖项添加到您的 build.gradle:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0"
await()
函数returns任务的结果,否则任务失败会抛出异常,所以应该用try/catch包起来,而不是用success和failure听众。
修复代码的方法如下:
class BarcodeActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val scanButton = findViewById<Button>(...)
scanButton.setOnClickListener {
lifecycleScope.launch {
val barcodes = scanBarcodes(image)
// do something with returned barcodes or maybe show message if list empty
}
}
}
}
// Package level function
suspend fun scanBarcodes(image: InputImage): List<Barcode> {
return try {
BarcodeScanning.getClient()
.process(image)
.await()
} catch(e: Exception) {
Log.e("BAR", "Barcode scan failed")
emptyList() // Returns an empty list on failure, but you might want to handle it differently, like returning null.
}
}