You need to enable JavaScript to run this app. Android Retrofit2调用enqueue后onResponse与onFailure未执行问题求助
优惠活动
大模型
产品
解决方案
定价
更多

Android Retrofit2调用enqueue后onResponse与onFailure未执行问题求助

问题根因

你的代码出现了死锁:

  • Retrofit 默认会把enqueue()的onResponse/onFailure回调投递到发起请求的线程的消息队列执行,如果你是在Android主线程(UI线程)发起的请求,回调必须等主线程空闲时才能执行
  • 你发起请求后直接在主线程调用了countDownLatch.await(),把主线程彻底阻塞等待计数器归零,但计数器的countDown()逻辑又必须等主线程执行回调才能触发,直接形成死锁,所以你会看到OkHttp日志已经拿到响应,但回调永远不会执行。

解决方案

有两种通用解决方式,按需选择:

方案1:放弃CountDownLatch,直接在回调中处理后续逻辑(最推荐)

Android本身禁止阻塞主线程,否则会触发ANR,你可以直接把登录成功后存Token、跳转页面的逻辑写到onResponse内,失败提示逻辑写到onFailure内,不需要阻塞线程等待结果:

Call<LoginResponse> loginResponseCall = service.getAuthToken(new LoginRequest(username,password));
loginResponseCall.enqueue(new Callback<LoginResponse>() {
    @Override
    public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
        Log.d("#Login", "Result: " + response);
        if (response.isSuccessful()) {
            if (response.body() != null) {
                loggedInUser = new LoggedInUser(response.body());
                // 此处直接写登录成功后续逻辑:比如持久化Token、跳转首页
            }
        }else {
            Log.e("#Login", "error: "+ (response.errorBody() != null ? response.errorBody().string() : "未知错误"));
            // 此处写登录失败提示:比如Toast弹出账号密码错误提示
        }
    }

    @Override
    public void onFailure(Call<LoginResponse> call, Throwable t) {
        Log.e("#Login", "请求失败", t);
        // 此处写网络异常提示逻辑
    }
});
// 不要加任何阻塞等待代码,直接结束该段逻辑即可

注意:原代码错误分支取response.body()是错误的,非2xx响应的内容需要从response.errorBody()中获取

方案2:必须同步等待结果时,切换到子线程执行请求

如果你的场景确实需要同步获取请求结果,把整个请求逻辑放到子线程执行,不要阻塞主线程,也可以直接用Retrofit的同步执行方法execute(),代码更简洁:

// 放到子线程执行,也可以用RxJava的IO线程、协程等实现
new Thread(() -> {
    try {
        Response<LoginResponse> response = service.getAuthToken(new LoginRequest(username,password)).execute();
        Log.d("#Login", "Result: " + response);
        if (response.isSuccessful()) {
            if (response.body() != null) {
                loggedInUser = new LoggedInUser(response.body());
                // 后续需要操作UI时,切回主线程即可
            }
        }else {
            Log.e("#Login", "error: "+ (response.errorBody() != null ? response.errorBody().string() : "未知错误"));
        }
    } catch (IOException e) {
        Log.e("#Login", "请求失败", e);
    }
}).start();

额外优化建议

  • 当前Retrofit实例为单例,但getRetrofitInstance方法支持传入url参数,如果你后续传入不同的url,会一直复用第一次初始化的实例的baseUrl,不符合预期,建议要么把baseUrl写死在实例化逻辑中,要么放弃单例模式。
  • 你的LoginService里的refresh接口返回值是RxJava的Observable,调用时需要指定subscribeOn和observeOn线程,否则也会出现类似的主线程阻塞问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 05:45:05