4

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)

Cătălin Florescu
  • 5,012
  • 1
  • 25
  • 36
Saleh
  • 1,819
  • 1
  • 17
  • 44

2 Answers2

12

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

3

Body from Retrofit response can be accessed just one time, is cleared after is readed, in your case logged to console second time.

From documentation:

A one-shot stream from the origin server to the client application with the raw bytes of the response body.

More details on official documentation.

You should keep using the saved one, the myResponse variable.

Cătălin Florescu
  • 5,012
  • 1
  • 25
  • 36