Callback failure for canceled call in OkHttp

喜夏-厌秋 提交于 2020-01-23 12:25:13

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!