如何等待OKHTTP响应结果且不导致应用冻结
问题根因
应用冻结无响应的核心原因是你在UI主线程调用了CountDownLatch.await()方法。await()会直接阻塞当前执行线程,直到计数器归零才会放行后续逻辑。而移动端的UI主线程需要持续处理界面绘制、用户输入事件、系统消息分发,一旦被阻塞,所有UI相关操作都会被挂起,直接表现为应用卡死无响应,直到OkHttp请求完成、触发countDown()让await()返回后,主线程才能恢复工作。
本质是你试图通过CountDownLatch把OkHttp的异步请求强行转为同步调用,且阻塞了不该阻塞的主线程。
可行解决方案
- 优先直接使用OkHttp原生异步能力,废弃CountDownLatch阻塞逻辑
不需要额外加锁等待,直接调用OkHttp的enqueue()方法发起异步请求,在回调中处理结果即可。如果需要更新UI,在回调中切回主线程执行即可,从根源上消除线程阻塞。
代码示例:OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("你的目标请求地址") .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { // 处理请求失败逻辑,更新UI需切主线程 } @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { // 处理响应结果,更新UI需切主线程 } }); - 特殊场景必须同步等待时,将阻塞逻辑全部移到子线程
如果业务逻辑强依赖同步等待的写法(比如多请求依赖串联场景),绝对不要在主线程调用await(),要把请求、CountDownLatch等待的全量逻辑放到工作子线程执行,等子线程拿到结果后,再通过Handler、协程、LiveData等线程切换机制把结果传回主线程更新UI。
注意await()必须设置超时时间,避免网络异常导致线程永久阻塞:new Thread(() -> { CountDownLatch latch = new CountDownLatch(1); // 执行OkHttp请求,在请求成功/失败的回调中调用latch.countDown() try { // 设置15秒超时,避免无限等待 boolean waitSuccess = latch.await(15, TimeUnit.SECONDS); if (!waitSuccess) { // 处理请求超时逻辑 } // 拿到结果后切回主线程更新UI } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 处理线程中断异常 } }).start(); - Kotlin开发场景优先用协程替代CountDownLatch
用协程的suspendCancellableCoroutine把OkHttp回调封装成挂起函数,既可以写线性的同步风格代码,又不会实际阻塞线程,实现比CountDownLatch更优雅,也不会触发主线程阻塞问题。
开发注意:所有会阻塞线程的操作,包括网络请求、本地IO、耗时计算、CountDownLatch/Future这类阻塞等待方法,都永远不要直接在UI主线程执行,这是移动端开发的基本准则,遵守该准则可以避免绝大多数应用无响应问题。
内容的提问来源于stack exchange,提问作者TriangleN
相关产品推荐
相关产品推荐

