java.lang.IllegalStateException 因为 okhttp 异步 HTTP POST 请求中的 onResponse 代码
java.lang.IllegalStateException because of onResponse code in okhttp async HTTP POST request
我在这里写了一段代码:
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;
}
}
当我 运行 这个时,令人惊讶的是我得到了 java.lang.IllegalStateException。更令人惊奇的是,如果我删除第二行 Log.d,异常将不会发生!
发生了什么事?为什么在 onResponse 中添加虚拟行会导致此错误?
这里是完整的错误日志:
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)
来自 Retrofit
响应的正文只能访问一次,在读取后被清除,在您的情况下第二次登录到控制台。
来自文档:
A one-shot stream from the origin server to the client application with the raw bytes of the response body.
有关 official documentation 的更多详细信息。
您应该继续使用保存的 myResponse
变量。
您正在使用 response.body().string()
两次
来自 OkHttp 3 documentation:
响应体只能被消费一次
您可以创建局部变量并使用它
@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();
}
}
}
或者你可以复制 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);
}
但要注意正确使用bufferSize
,防止OutOfMemoryError
P.s。您不需要将字符串记录到 Logcat。有一些更有效的调试 OkHttp 客户端的方法,例如
- https://github.com/itkacher/OkHttpProfiler - Android studio 的插件,用于分析 OkHttp 请求
- https://www.charlesproxy.com/ - Proxy application for http (setup manual)
- http://facebook.github.io/stetho/ - Android 应用程序的调试桥
我在这里写了一段代码:
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;
}
}
当我 运行 这个时,令人惊讶的是我得到了 java.lang.IllegalStateException。更令人惊奇的是,如果我删除第二行 Log.d,异常将不会发生!
发生了什么事?为什么在 onResponse 中添加虚拟行会导致此错误?
这里是完整的错误日志:
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)
来自 Retrofit
响应的正文只能访问一次,在读取后被清除,在您的情况下第二次登录到控制台。
来自文档:
A one-shot stream from the origin server to the client application with the raw bytes of the response body.
有关 official documentation 的更多详细信息。
您应该继续使用保存的 myResponse
变量。
您正在使用 response.body().string()
两次
来自 OkHttp 3 documentation: 响应体只能被消费一次
您可以创建局部变量并使用它
@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();
}
}
}
或者你可以复制 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);
}
但要注意正确使用bufferSize
,防止OutOfMemoryError
P.s。您不需要将字符串记录到 Logcat。有一些更有效的调试 OkHttp 客户端的方法,例如
- https://github.com/itkacher/OkHttpProfiler - Android studio 的插件,用于分析 OkHttp 请求
- https://www.charlesproxy.com/ - Proxy application for http (setup manual)
- http://facebook.github.io/stetho/ - Android 应用程序的调试桥