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
相关产品推荐
相关产品推荐

