Search code examples
androidretrofit2okhttpleanback

java.lang.IllegalStateException because of onResponse code in okhttp async HTTP POST request


I have written a code here:

public class Wizard1 extends GuidedStepFragment implements Callback {

    private boolean sendPhoneNumber(String userPhoneNumber) {

        OkHttpClient client = new OkHttpClient();

        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("request_magic_code", Urls.REQUEST_MAGIC_CODE)
                .build();

        Request request = new Request.Builder()
                .url(Urls.HOST + Urls.SEND_PHONE_NUMBER)
                .post(requestBody)
                .build();

        client.newCall(request).enqueue(this);
        return success;
    }

    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
        e.printStackTrace();
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
        ResponseBody myResponse = response.body();
        Log.d("SS", response.body().string());

    Log.d("SS", response.body().string());

        if (myResponse != null) {
            success = true;
            }

}

When I run this, amazingly I get java.lang.IllegalStateException. More amazingly, IF I REMOVE SECOND Log.d LINE, THE EXCEPTION WILL NOT OCCUR!

What are happening? Why adding a dummy line in onResponse causes this error?

here is the full log of error:

10-24 05:16:38.307 6639-6659/com.example.android.persistence W/System.err: java.lang.IllegalStateException: closed at okio.RealBufferedSource.rangeEquals(RealBufferedSource.java:398) at okio.RealBufferedSource.rangeEquals(RealBufferedSource.java:392) at okhttp3.internal.Util.bomAwareCharset(Util.java:431) at okhttp3.ResponseBody.string(ResponseBody.java:174) 10-24 05:16:38.308 6639-6659/com.example.android.persistence W/System.err:
at android.support.v17.leanback.supportleanbackshowcase.app.wizard.WizardGetPhoneNumber.onResponse(WizardGetPhoneNumber.java:244) at okhttp3.RealCall$AsyncCall.execute(RealCall.java:141) at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761)


Solution

  • You are using response.body().string() twice

    From the OkHttp 3 documentation: The response body can be consumed only once.

    You can create local variable and use it

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
        ResponseBody body = response.body();
        if(body != null) {
            try {
                //Use it anytime you want
                String responseString = body.string();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    Or you can copy ResponseBody

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
        ResponseBody body = response.body();
        //Warning: this method loads the requested bytes into memory. Most
        // applications should set a modest limit on {@code byteCount}, such as 1 MiB.
        int bufferSize = 1024 * 1024;
        ResponseBody copy = response.peekBody(bufferSize);
    }
    

    But be careful to use bufferSize correctly, to prevent OutOfMemoryError

    P.s. you don't need to log string to Logcat. There are few more efficient ways to debug OkHttp client such as