问题
Canceling enqueued calls in OkHttp (version 2.4) client sometimes throws Callback failure for canceled call to http://...
and neither of callback methods gets executed.
Since I have to provide either positive or negative feedback to the user, having callback eaten up like that is not acceptable. How can I get around that error?
I have created test application with two buttons (one for starting download request, another for canceling it), text view and image view, that shows error most of the time. Originally, I have caught error clicking on Cancel button, but using auto-click with handler is much more effective for repeating the error successfully.
package com.test.httptest;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.IOException;
public class MainActivity extends Activity
{
final private OkHttpClient client = new OkHttpClient();
private Object cancel = new Object();
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onHttpClick(View v)
{
TextView tv = (TextView) findViewById(R.id.text_view);
tv.setText("start");
downloadImage("http://httpbin.org/image/jpeg");
// simulate cancel click
new Handler().postDelayed(new Runnable()
{
public void run()
{
onCancelClick(null);
}
}, 380);
}
public void onCancelClick(View v)
{
TextView tv = (TextView) findViewById(R.id.text_view);
tv.setText("canceling");
client.cancel(cancel);
}
public void downloadImage(String url)
{
Request request = new Request.Builder()
.tag(cancel)
.url(url)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback()
{
String result = "";
@Override
public void onFailure(Request request, IOException e)
{
result = e.getMessage();
runOnUiThread(new Runnable()
{
@Override
public void run()
{
TextView tv = (TextView) findViewById(R.id.text_view);
tv.setText(result);
}
});
}
@Override
public void onResponse(Response response) throws IOException
{
if (response.isSuccessful())
{
final byte[] input = response.body().bytes();
final int len = (int) response.body().contentLength();
runOnUiThread(new Runnable()
{
@Override
public void run()
{
TextView tv = (TextView) findViewById(R.id.text_view);
tv.setText("completed");
ImageView iv = (ImageView) findViewById(R.id.image_view);
Bitmap bm = BitmapFactory.decodeByteArray(input, 0, len);
iv.setImageBitmap(bm);
}
});
}
else
{
result = response.body().string();
runOnUiThread(new Runnable()
{
@Override
public void run()
{
TextView tv = (TextView) findViewById(R.id.text_view);
tv.setText(result);
}
});
}
}
});
}
}
Logcat:
08-24 23:00:13.151 14403-14451/com.test.httptest I/OkHttpClient﹕ Callback failure for canceled call to http://httpbin.org/...
java.net.SocketException: Socket closed
at libcore.io.Posix.recvfromBytes(Native Method)
at libcore.io.Posix.recvfrom(Posix.java:141)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240)
at okio.Okio$2.read(Okio.java:137)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
at okio.RealBufferedSource.read(RealBufferedSource.java:50)
at com.squareup.okhttp.internal.http.HttpConnection$FixedLengthSource.read(HttpConnection.java:418)
at okio.Buffer.writeAll(Buffer.java:956)
at okio.RealBufferedSource.readByteArray(RealBufferedSource.java:92)
at com.squareup.okhttp.ResponseBody.bytes(ResponseBody.java:57)
at hr.artplus.httptestm.MainActivity$2.onResponse(MainActivity.java:87)
at com.squareup.okhttp.Call$AsyncCall.execute(Call.java:170)
at com.squareup.okhttp.internal.NamedRunnable.run(NamedRunnable.java:33)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
回答1:
It is calling your onResponse
method. It appears in the stack trace as
at hr.artplus.httptestm.MainActivity$2.onResponse(MainActivity.java:87)
What is happening is that the you are getting an IOException
on this line --
final byte[] input = response.body().bytes();
probably because the stream is closed while you were reading it due to the cancellation. Your code lets the exception propagate back up to the Callback
class, which does not issue another callback because it already called onResponse
.
You can catch the exception and deal with it in your callback --
if (response.isSuccessful()) {
try {
final byte[] input = response.body().bytes();
final int len = (int) response.body().contentLength();
} catch (IOException e) {
// Signal to the user failure here, re-throw the exception, or
// whatever else you want to do on failure
}
...
来源:https://stackoverflow.com/questions/32191868/callback-failure-for-canceled-call-in-okhttp