OkHttpClient 有时会得到不完整的 json 响应

OkHttpClient sometimes getting incomplete json response

我一直面临这个不完整的 json 错误,无法找到问题所在。 API 响应在 POSTMAN 中工作正常。但是这个问题发生在我的 android 模拟器中,而且它只是随机发生的。这个项目是用 kotlin dagger-hilt retrofit2 okhttp3 gson 构建的。

成功响应

I/okhttp.OkHttpClient: <-- 200 OK http://10.0.2.2:8000/api/v1/user/friend/mutual?name=as (217ms)
I/okhttp.OkHttpClient: Host: 10.0.2.2:8000
I/okhttp.OkHttpClient: Date: Thu, 03 Mar 2022 06:38:59 GMT
I/okhttp.OkHttpClient: Connection: close
I/okhttp.OkHttpClient: X-Powered-By: PHP/7.4.13
I/okhttp.OkHttpClient: Cache-Control: no-cache, private
I/okhttp.OkHttpClient: Date: Thu, 03 Mar 2022 06:38:59 GMT
I/okhttp.OkHttpClient: Content-Type: application/json
I/okhttp.OkHttpClient: X-RateLimit-Limit: 60
I/okhttp.OkHttpClient: X-RateLimit-Remaining: 52
I/okhttp.OkHttpClient: Access-Control-Allow-Origin: *
I/okhttp.OkHttpClient: {"friends":[{"user_id":16,"name":"Grant Erdman","username":"lilla.kulas","email":"claudie04@example.org","image":"https:\/\/via.placeholder.com\/640x480.png\/00dd44?text=dolor","relationship":"friend"}],"requests":[]}
I/okhttp.OkHttpClient: <-- END HTTP (217-byte body)

不完整Json 缺少响应“}”

I/okhttp.OkHttpClient: <-- 200 OK http://10.0.2.2:8000/api/v1/user/friend/mutual?name=as (198ms)
I/okhttp.OkHttpClient: Host: 10.0.2.2:8000
I/okhttp.OkHttpClient: Date: Thu, 03 Mar 2022 06:39:00 GMT
I/okhttp.OkHttpClient: Connection: close
I/okhttp.OkHttpClient: X-Powered-By: PHP/7.4.13
I/okhttp.OkHttpClient: Cache-Control: no-cache, private
I/okhttp.OkHttpClient: Date: Thu, 03 Mar 2022 06:39:00 GMT
I/okhttp.OkHttpClient: Content-Type: application/json
I/okhttp.OkHttpClient: X-RateLimit-Limit: 60
I/okhttp.OkHttpClient: X-RateLimit-Remaining: 51
I/okhttp.OkHttpClient: Access-Control-Allow-Origin: *
I/okhttp.OkHttpClient: {"friends":[{"user_id":16,"name":"Grant Erdman","username":"lilla.kulas","email":"claudie04@example.org","image":"https:\/\/via.placeholder.com\/640x480.png\/00dd44?text=dolor","relationship":"friend"}],"requests":[]
I/okhttp.OkHttpClient: <-- END HTTP (216-byte body)
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.findplan, PID: 4695
    java.io.EOFException: End of input at line 1 column 217 path $.requests
        at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1395)
        at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:481)
        at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:413)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:216)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
        at com.example.findplan.utils.NullOnEmptyConverterFactory.convert(NullOnEmptyConverterFactory.java:20)
        at com.example.findplan.utils.NullOnEmptyConverterFactory.convert(NullOnEmptyConverterFactory.java:16)
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
        at retrofit2.OkHttpCall.onResponse(OkHttpCall.java:153)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
I/Process: Sending signal. PID: 4695 SIG: 9

我的网络模块

@Singleton
    @Provides
    fun provideOkHttpClient(mySharedPreferences: MySharedPreferences): OkHttpClient {
        val builder = OkHttpClient
            .Builder()
            .connectTimeout(1, TimeUnit.MINUTES)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(15, TimeUnit.SECONDS)
        val authenticationInterceptor = AuthenticationInterceptor(mySharedPreferences)
        builder.addInterceptor(authenticationInterceptor)
        val interceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);
        builder.addNetworkInterceptor(interceptor)

        return builder.build()
    }

    @Singleton
    @Provides
    fun provideGson(): Gson {
        return GsonBuilder()
            .setLenient()
            .create()
    }

    @Singleton
    @Provides
    fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BuildConfig.BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(NullOnEmptyConverterFactory())
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
    }

NullOnEmptyConverterFactory

public class NullOnEmptyConverterFactory extends Converter.Factory {

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
        return new Converter<ResponseBody, Object>() {
            @Override
            public Object convert(ResponseBody body) throws IOException {
                if (body.contentLength() == 0) return null;
                return delegate.convert(body);
            }
        };
    }
}

我怀疑 Android 模拟器可能会在这里干扰您。我已经看到它行为不端的问题,尤其是在 Windows.

https://issuetracker.google.com/issues/119027639

如果您想解决此问题,请考虑将您的服务器更改为使用 Connection: close 以外的其他内容来终止您的响应 body。也许分块编码或 content-length header.