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

CompletableFuture致UI冻结,Unirest异步请求实现方案合理性咨询

API异步请求实现问题解答

原始代码的核心问题

你的代码结构思路没问题,但executeAsync方法里调用req.asObjectAsync(responseType).get()是错误的——get()会阻塞当前线程(也就是UI线程),直接导致UI冻结,完全违背了异步请求的初衷。

结构是否过于复杂?

你想引入Executor的修改思路反而增加了不必要的复杂度,还存在隐患:每次调用Executors.newSingleThreadScheduledExecutor()都会新建一个线程池,频繁调用会导致线程资源耗尽,甚至出现内存泄漏。

是否必须使用线程/Executor?

不需要。Unirest的asObjectAsync本身就返回CompletableFuture,它内部已经封装了异步执行的线程池,你不需要手动创建额外线程,只需要正确使用CompletableFuture的异步回调方法即可。

更优的实现方式

利用CompletableFuture的whenComplete、thenAccept等异步回调方法,避免调用阻塞式的get(),同时保证UI操作在UI线程执行。

修改后的Request类核心代码

fun executeAsync(action: (HttpResponse<T>) -> Unit, onError: (Throwable) -> Unit = {}) {
    prepareRequest { req ->
        // 直接使用Unirest返回的CompletableFuture,添加异步回调
        req.asObjectAsync(responseType)
            .whenComplete { res, exception ->
                if (exception != null) {
                    onError(exception)
                } else {
                    action(res)
                }
            }
    }
}

ApiClient类优化(增加错误处理)

abstract class ApiClient {
    protected fun <T> executeAsync(
        req: Request<T>,
        onSuccess: (T) -> Unit,
        onError: (Throwable) -> Unit = {}
    ) {
        req.executeAsync({ res ->
            if (res.isSuccess) {
                onSuccess(res.body)
            } else {
                onError(RuntimeException("请求失败,状态码:${res.status}"))
            }
        }, onError)
    }
}

AuthClient类(确保UI操作在UI线程)

以Android客户端为例,需要把onSuccess切换到UI线程执行:

class AuthClient : ApiClient() {
    fun signin(
        email: String,
        password: String,
        onSuccess: () -> Unit,
        onError: (Throwable) -> Unit = {}
    ) {
        executeAsync(
            Request.builder(TokenModel::class.java)
                .post("/signin")
                .body(SignInModel(email, password))
                .build(),
            {
                // 切换到UI线程执行刷新操作
                runOnUiThread { onSuccess() }
            },
            onError
        )
    }

    // 非Android环境可替换为对应平台的UI线程调度方法
}

进阶:结合Kotlin协程(更简洁的异步处理)

如果项目使用Kotlin协程,可以进一步简化代码,摆脱回调地狱:

  1. 将Request的异步方法封装为挂起函数:
suspend fun execute(): HttpResponse<T> {
    return prepareRequest { req ->
        req.asObjectAsync(responseType).await() // 使用kotlinx-coroutines-jdk8的await()
    }
}
  1. ApiClient的执行方法改为挂起函数:
abstract class ApiClient {
    protected suspend fun <T> execute(req: Request<T>): T {
        val res = req.execute()
        if (res.isSuccess) {
            return res.body
        } else {
            throw RuntimeException("请求失败,状态码:${res.status}")
        }
    }
}
  1. AuthClient的调用逻辑:
class AuthClient : ApiClient() {
    fun signin(
        email: String,
        password: String,
        onSuccess: () -> Unit,
        onError: (Throwable) -> Unit = {}
    ) {
        // 在IO协程执行网络请求,切换到Main线程处理UI
        CoroutineScope(Dispatchers.IO).launch {
            try {
                execute(
                    Request.builder(TokenModel::class.java)
                        .post("/signin")
                        .body(SignInModel(email, password))
                        .build()
                )
                withContext(Dispatchers.Main) { onSuccess() }
            } catch (e: Exception) {
                withContext(Dispatchers.Main) { onError(e) }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 17:45:28