SocketException при отмене вызова с использованием OkHttp

Следующий код представляет загрузку файла. Вызов будет отменен, когда Observable будет удален. В моем случае Observable будет размещен в методе onDestroy() Фрагмента (поскольку ему не нужно будет продолжать загрузку файла на Фрагменте, который уничтожен или будет уничтожен). Всякий раз, когда происходит сценарий отмены во время процесса загрузки файла, этот код аварийно завершает работу с SocketException. Сообщение может быть либо "Socket Closed", либо "Socket Operation on non-socket". Мой вопрос: как правильно отменить текущий запрос на загрузку с помощью OkHttp?

Код:

Request request = //..
final Call call = client.newCall(request);

   call.enqueue(new Callback() {

       @Override
       public void onFailure(Call call, IOException error)
       {
          Log.e("Acme", "OnFailure:", error);
          emitter.onError(error);
       }

       @Override
       public void onResponse(Call call, Response response) throws IOException
       {
           if (response.isSuccessful())
           {
               BufferedSink sink = null;
               BufferedSource source = null;
               try
               {
                   Log.d("Acme", "onResponse: Start writing..");
                   sink = Okio.buffer(Okio.sink(file));
                   source = response.body().source();
                   sink.writeAll(source); // Crash on this line.
                   sink.flush();

                   emitter.onNext(file);
                   emitter.onComplete();
               }
               catch (IOException exception)
               {
                   Log.e("Acme", "onResponse: Exception writing file:", exception);
                   emitter.onError(exception);
               }
               finally
               {
                   Log.d("Acme", "onResponse: Trying to close..");
                   Util.closeQuietly(sink);
                   Util.closeQuietly(source);
                   Log.d("Acme", "onResponse: Closed..");
               }
           }
       }
   });

   emitter.setCancellable(new Cancellable() {

       @Override
       public void cancel() throws Exception 
       {
           if (!call.isCanceled()) 
           {
               call.cancel();
           }
       }
   });
}

Консоль:

D/Acme: onResponse: Start writing..
E/AndroidRuntime: FATAL EXCEPTION: OkHttp https://acme.nl/...
Process: nl.acme.app.development.debug, PID: 12432
java.net.SocketException: Socket operation on non-socket
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:151)
    at java.net.SocketInputStream.read(SocketInputStream.java:120)
    at okio.Okio$2.read(Okio.java:138)
    at okio.AsyncTimeout$2.read(AsyncTimeout.java:236)
    at okio.RealBufferedSource.read(RealBufferedSource.java:45)
    at okhttp3.internal.http1.Http1Codec$FixedLengthSource.read(Http1Codec.java:385)
    at okio.RealBufferedSource.read(RealBufferedSource.java:45)
    at okio.RealBufferedSink.writeAll(RealBufferedSink.java:97)
    at nl.app.acme.infrastructure.network.Api$44$1.onResponse(Api.java:3322)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
    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)

person Thomas Neuteboom    schedule 09.03.2017    source источник
comment
Показанный код не имеет ничего общего с rx-java или rx-android.   -  person Tassos Bassoukos    schedule 09.03.2017
comment
Можете ли вы прикрепить код метода Util.closeQuietly()?   -  person yosriz    schedule 09.03.2017
comment
Это работает так, как задумано. Когда вы отменяете вызов, его потоки вызывают исключения.   -  person Jesse Wilson    schedule 10.03.2017
comment
@JesseWilson Я знаю, что это работает по назначению, но выдает фатальное исключение, пока оно окружено предложением try/catch. Есть ли у вас какие-либо предложения о том, как это правильно?   -  person Thomas Neuteboom    schedule 14.03.2017
comment
@ThomasNeuteboom, проблема может заключаться в Util.closeQuietly(sink); Util.closeQuietly(источник); методы, вы можете выполнять операции с закрытым потоком, которые вызывают исключение в предложении finally, пожалуйста, приложите код этих методов, кроме того, ваше исключение происходит в строке 3322 (??) вашего API.java, что это за строка точно?   -  person yosriz    schedule 15.03.2017
comment
@yosriz Привет, Йосриз, вылетает строка (3322) — sin.writeAll(). Метод Util closeQuitely взят из библиотеки OkHttp. Он просто закрывает Closeable (интерфейс, содержащий метод close()), игнорируя любые проверенные исключения. Ничего не делает, если Closeable имеет значение null. Проблема сохраняется при использовании «нормального» пути закрытия приемника/источника.   -  person Thomas Neuteboom    schedule 16.03.2017
comment
Привет, это может быть старо, но я столкнулся с той же проблемой, и, возможно, это кому-то поможет. Я окружил sink.writeAll() с помощью try/catch(e: SocketException) в отличие от OP, который ловит IOException, и, похоже, это решило проблему с моей стороны.   -  person MikeOscarEcho    schedule 31.10.2017


Ответы (1)


В коде

catch (IOException exception)
{
    Log.e("Acme", "onResponse: Exception writing file:", exception);
    emitter.onError(exception);
}

вы отправляете onError() эмиттеру, который уже отписался, пока вы уничтожаете фрагмент. Пожалуйста, проверьте:

if (!emitter.isDisposed()) emitter.onError(exception);
person Nistix    schedule 16.02.2018