我最近与Kotlin一起工作,并真的陷入了这一问题。我试图返回协程api调用函数的float值接收onResponse。我正在尝试创建一个处理api调用的类,并将其用于片段。
FunctionA.kt
class FunctionA(val context: Context?, val A: Float?, val B: String?){
private var cardApi: CardApi = ApiClient.createApi().create(CardApi::class.java)
....
func getBalance(cardNo: String): Float?{
val cardBalance: Float = null
GlobalScope.launch(Dispatchers.Main) {
val cardDetails = cardApi.getCardBalance(cardNo)
cardDetails.enqueue(object : Callback<Card> {
override fun onFailure(call: Call<Card>, t: Throwable) {
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to t.message!!)
}
override fun onResponse(call: Call<Card>, response: Response<Card>) {
if (response.isSuccessful) {
val card = response.body()!!
cardBalance = card.cardAvailableBalance
} else {
val error: ApiError = ErrorUtils.parseError(response)
val message = error.code + error.message
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to message)
context!!.toast("Errror: " + message)
promptErrorDialog(error)
}
}
})
}}
return cardBalance
}
....
....
}
FragmentClass.kt
class FragmentClass : BaseFragment(){
val galA = 10.5f
val galB = "Test"
private var pass = FunctionA(context!!, valA ,valB)
....
val point = "sasd12125"
private fun gooToo(){
val B = pass.getBalance(point)
print("TEST")
println("value B: " + B)
}
....
}
现在发生了什么,因为协程会在后台花费一些时间,val B
为空,并且没有获得onResponse上获得的值。只有在我尝试再次调用该functionA之后,该值才会更新。我不确定我是否做对了,我已经尝试寻找解决方案,但是它不适合我目前的情况。可能我的搜索技能太差了。
输出
TEST
value B: null
在返回cardBalance
值之前,我应该如何等待协程完成?
答案 0 :(得分:1)
从协程返回单个值的正确方法是使用await()
。
现在,由于您使用协程包装了一些回调API,因此效果不佳。因此,我建议您使用类似这样的内容:
val scope = CoroutineScope(Dispatchers.IO)
suspend fun getBalance(cardNo: String): Float{
val res = CompletableDeferred<Float>()
scope.launch {
val cardDetails = cardApi.getCardBalance(cardNo)
cardDetails.enqueue(object : Callback<Card> {
override fun onFailure(call: Call<Card>, t: Throwable) {
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to t.message!!)
}
override fun onResponse(call: Call<Card>, response: Response<Card>) {
if (response.isSuccessful) {
val card = response.body()!!
res.complete(card.cardAvailableBalance)
} else {
val error: ApiError = ErrorUtils.parseError(response)
val message = error.code + error.message
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to message)
res.completeExceptionally(message)
withContext(Dispatchers.Main) {
promptErrorDialog(error)
}
}
}
})
}
return res.await()
}
需要考虑的几点。首先,我使用Dispatchers.IO
而不是Dispatchers.Main
,并且仅在需要时使用Main
切换到withContext(Dispatchers.Main)
线程。否则,您只是在主线程上运行IO,无论协程是否正常。
第二,使用GlobalScope
是一个不好的做法,您应该不惜一切代价避免使用它。相反,我创建了一个自定义范围,您可以.cancel()
来防止协程泄漏。
第三,最正确的方法是返回Deferred<Float>
,而不是Float
,因为await()
被阻止了。但是为了简单起见,我将其保留。
答案 1 :(得分:0)
使getBalance()
成为暂停函数,然后在片段中使用lifecycleScope
进行调用
private fun gooToo(){
lifecycleScope.launch {
val B = pass.getBalance(point)
print("TEST")
println("value B: " + B)
}
}
getBalance()
函数签名将类似于
suspend fun getBalance(): Float = withContext(Dispatchers.IO)