Android用Retrofit2+OkHttp3请求Laravel接口返回空JSONObject问题
问题分析与解决方案
先梳理下你代码里的两个核心问题,这正是导致响应体为空的关键原因:
1. OkHttp拦截器未继续执行请求
你的Interceptor实现漏掉了最关键的一步——没有返回chain.proceed(request),这会导致请求被拦截后无法发送到服务器,虽然日志显示200状态码,但实际上可能是无效的缓存或空响应。
修复方式:在Interceptor的intercept方法末尾添加返回语句:
httpClient.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); Request request = original.newBuilder() .header("Accept", "application/json") .header("Content-Type", "application/json") .header("Authorization", "Bearer " + token) .build(); // 必须添加这行,让请求继续流向服务器 return chain.proceed(request); } });
2. Gson无法正确解析JSONObject类型
Retrofit的GsonConverterFactory默认会将JSON数据解析为对应Java实体类,而非org.json.JSONObject。当你用Call<JSONObject>作为返回类型时,Gson无法正确映射JSON结构,最终得到空的JSONObject实例。
解决这个问题有两种推荐方案:
方案一:使用ResponseBody手动解析
修改接口定义返回ResponseBody,在回调中手动转换为JSONObject:
// SvcApi接口修改 @GET("api/me") public Call<ResponseBody> getMe(@Query("device_id") String device_id);
然后在Activity的回调中处理:
Call<ResponseBody> call = svcAth.getMe(Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID)); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { try { // 读取响应字符串并手动解析为JSONObject String responseContent = response.body().string(); JSONObject user = new JSONObject(responseContent); Log.e("完整响应体", user.toString()); // 在这里处理用户数据 } catch (IOException | JSONException e) { e.printStackTrace(); } } else { // 处理错误情况 } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { t.printStackTrace(); } });
方案二:创建实体类接收数据(推荐)
根据Laravel/api/me接口返回的JSON结构,创建对应的Java实体类,让Gson自动映射数据,这更符合Retrofit的设计理念,也更易维护。
比如假设你的接口返回如下JSON:
{ "id": 1, "name": "Alberto", "email": "alberto@example.com", "created_at": "2018-05-19T21:48:22Z" }
创建User类:
public class User { private int id; private String name; private String email; @SerializedName("created_at") // 对应JSON中的下划线字段 private String createdAt; // 生成getter和setter方法 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getCreatedAt() { return createdAt; } public void setCreatedAt(String createdAt) { this.createdAt = createdAt; } }
修改接口定义:
@GET("api/me") public Call<User> getMe(@Query("device_id") String device_id);
在回调中直接使用实体类:
Call<User> call = svcAth.getMe(Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID)); call.enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { if (response.isSuccessful()) { User user = response.body(); if (user != null) { Log.e("用户名", user.getName()); Log.e("邮箱", user.getEmail()); // 处理用户数据 } } } @Override public void onFailure(Call<User> call, Throwable t) { t.printStackTrace(); } });
额外调试建议
添加OkHttp日志拦截器,查看完整的请求和响应内容,确认服务器是否真的返回了数据:
- 在
build.gradle中添加依赖:
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.9'
- 初始化OkHttpClient时添加日志拦截器:
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); // 设置日志级别为BODY,会打印请求和响应的完整内容 loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient.addInterceptor(loggingInterceptor);
这样你就能在LogCat中看到服务器返回的原始JSON数据,方便排查问题。
其他优化点
- 移除
StrictMode代码:在主线程执行数据库操作和Retrofit初始化不规范,容易导致ANR,建议将这些操作放到后台线程(比如AsyncTask或Coroutines)。 - 将数据库操作(
db.ExistsUser()和db.findUser())移到后台线程,避免阻塞主线程。
内容的提问来源于stack exchange,提问作者Alberto Crespo
相关产品推荐
相关产品推荐

