You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin协程API调用问题:UI先加载且距离计算返回Null

解决Kotlin协程+OkHttp异步导致返回值为Null的问题

我一眼就看出你遇到的核心问题了:OkHttp的异步回调和方法同步返回的矛盾。你原来的getPostcodeLocation方法里,client.newCall(request).enqueue()是异步执行的——也就是说,方法会直接跳过回调代码,先执行到return distance这一行,而此时回调还没跑完,distance还是初始的null,所以UI拿到的就是空值,自然渲染不出正确结果。

下面我给你一步步拆解解决方案,用协程把异步代码改成"看起来像同步"的写法,彻底解决这个问题:

第一步:把方法改成协程挂起函数

我们需要把getPostcodeLocation标记为suspend函数,并用withContext切换到IO线程执行网络请求(Android主线程不能做网络操作),同时把OkHttp的异步调用改成同步调用(在IO线程里同步执行不会阻塞UI):

private suspend fun getPostcodeLocation(listOfPostCodes: List<Pair<Boolean, String>>): String {
    val jsonList = JSONObject()
    jsonList.put("postcodes", JSONArray(listOfPostCodes.map { it.second }))
    val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonList.toString())
    val request = okhttp3.Request.Builder()
        .url("https://api.postcodes.io/postcodes")
        .post(body)
        .build()
    val client = OkHttpClient()

    return withContext(Dispatchers.IO) {
        try {
            // 用execute()同步执行请求,代替异步的enqueue()
            val response = client.newCall(request).execute()
            val responseBody = response.body()?.string() ?: return@withContext "Not Available"
            
            val jsonTree = JsonParser().parse(responseBody).asJsonObject
            val resultJsonArray = jsonTree.getAsJsonArray("result")
            
            val listOfLonLat = mutableListOf<PostcodeLongLat>()
            resultJsonArray.forEach {
                val resultObject = it.asJsonObject.getAsJsonObject("result")
                val lat = resultObject.get("latitude").asDouble
                val lon = resultObject.get("longitude").asDouble
                listOfLonLat.add(PostcodeLongLat(lon, lat))
            }
            
            // 计算距离后返回结果
            calculateDistance(listOfLonLat)
        } catch (e: IOException) {
            Log.d("PostCodeChecker", "Failed to get the information from the API", e)
            "Not Available"
        }
    }
}

第二步:在Activity的协程作用域里调用挂起函数

因为挂起函数不能在普通函数里直接调用,我们需要用Activity的生命周期协程作用域lifecycleScope(需要依赖androidx.lifecycle:lifecycle-runtime-ktx库)来调用它,拿到结果后再更新UI:

// 比如在Activity的onCreate方法里,或者按钮点击事件中
lifecycleScope.launch {
    // 这里会挂起直到getPostcodeLocation返回结果
    val distanceResult = getPostcodeLocation(yourPostCodeList)
    // 拿到结果后再更新UI,确保UI渲染的是正确值
    yourDistanceTextView.text = distanceResult
}

为什么这样改能解决问题?

  • 挂起函数的特性:suspend函数会在执行到耗时操作(比如网络请求)时挂起,直到操作完成再返回结果,不会像原来的异步回调那样让方法提前返回null。
  • 线程切换安全:withContext(Dispatchers.IO)把网络请求放到IO线程执行,既遵守了Android的网络操作规则,又不会阻塞主线程导致UI卡顿。
  • 生命周期绑定:lifecycleScope会和Activity的生命周期同步,避免Activity销毁后协程还在执行导致内存泄漏。

这样修改后,你就能确保UI在拿到正确的距离结果后再进行渲染,再也不会出现返回null的问题了。

内容的提问来源于stack exchange,提问作者OrangeSubmarine121

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:22:18