So I have a simple http request using OkHttp. I do this with RxJava on Android. I add this RxJava call to a CompositeDisposable that I then clear on onStop. For some reason that is triggering this exception below. I'm a little unsure about how to fix it.
Caused by java.lang.IllegalStateException: Unbalanced enter/exit
at okio.AsyncTimeout.enter(AsyncTimeout.java:73)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:235)
at okio.RealBufferedSource.read(RealBufferedSource.java:47)
at okhttp3.internal.http1.Http1Codec$AbstractSource.read(Http1Codec.java:363)
at okhttp3.internal.http1.Http1Codec$FixedLengthSource.read(Http1Codec.java:407)
at okio.RealBufferedSource.read(RealBufferedSource.java:47)
at okhttp3.internal.cache.CacheInterceptor$1.read(CacheInterceptor.java:174)
at okhttp3.internal.Util.skipAll(Util.java:175)
at okhttp3.internal.Util.discard(Util.java:157)
at okhttp3.internal.cache.CacheInterceptor$1.close(CacheInterceptor.java:202)
at okio.RealBufferedSource.close(RealBufferedSource.java:469)
at okio.RealBufferedSource$1.close(RealBufferedSource.java:453)
at java.nio.channels.Channels$ReadableByteChannelImpl.implCloseChannel(Channels.java:255)
at java.nio.channels.spi.AbstractInterruptibleChannel$1.interrupt(AbstractInterruptibleChannel.java:166)
at java.lang.Thread.interrupt(Thread.java:957)
at java.util.concurrent.FutureTask.cancel(FutureTask.java:146)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.cancel(ScheduledThreadPoolExecutor.java:258)
at io.reactivex.internal.schedulers.ScheduledRunnable.dispose(ScheduledRunnable.java:107)
at io.reactivex.disposables.CompositeDisposable.dispose(CompositeDisposable.java:217)
at io.reactivex.disposables.CompositeDisposable.dispose(CompositeDisposable.java:80)
at io.reactivex.internal.schedulers.IoScheduler$EventLoopWorker.dispose(IoScheduler.java:210)
at io.reactivex.Scheduler$DisposeTask.dispose(Scheduler.java:464)
at io.reactivex.internal.disposables.DisposableHelper.dispose(DisposableHelper.java:125)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.dispose(ObservableSubscribeOn.java:74)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.dispose(ObservableObserveOn.java:146)
at io.reactivex.internal.disposables.DisposableHelper.dispose(DisposableHelper.java:125)
at io.reactivex.observers.DisposableObserver.dispose(DisposableObserver.java:91)
at io.reactivex.disposables.CompositeDisposable.dispose(CompositeDisposable.java:217)
at io.reactivex.disposables.CompositeDisposable.clear(CompositeDisposable.java:183)
at MyActivity.onStop(MyActivity.java:320)
at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1297)
at android.app.Activity.performStop(Activity.java:7159)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4625)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4691)
at android.app.ActivityThread.-wrap7(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1718)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6732)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
The code is basically this:
Observable<Stuff> observable = Observable.create(new ObservableOnSubscribe<Stuff>() {
#Override
public void subscribe(#NonNull ObservableEmitter<Stuff> e) throws Exception {
//do OkHttp stuff, call onNext(stuff).
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
DisposableObserver<Stuff> disposableObserver = observable
.subscribeWith(new DisposableObserver<Stuff>() {......});
disposables.add(disposableObserver);
Theres a bug in some Android VMs that interacts poorly with the new D8 compiler.
https://github.com/square/okhttp/issues/3641#issuecomment-370717021
Google has recently improved D8 to avoid triggering the bug.
Related
I am using the following (out of date) libraries:
Retrofit: 1.9.0
OkHTTP: 2.3.0
RxAndroid: 0.24.0
I noticed that every once in a while I get the following stack trace for my POST request:
D/Retrofit: ---> HTTP POST https:xxxxx
D/Retrofit: Content-Type: application/x-www-form-urlencoded; charset=UTF-8
D/Retrofit: Content-Length: 396
D/Retrofit: ---> END HTTP (396-byte body)
D/Retrofit: ---- ERROR https:xxxxx
I/Choreographer: Skipped 33 frames! The application may be doing too much work on its main thread.
D/Retrofit: java.io.InterruptedIOException
at okio.Timeout.throwIfReached(Timeout.java:146)
at okio.Okio$1.write(Okio.java:75)
at okio.AsyncTimeout$1.write(AsyncTimeout.java:155)
at okio.RealBufferedSink.flush(RealBufferedSink.java:201)
at com.squareup.okhttp.internal.http.HttpConnection.flush(HttpConnection.java:140)
at com.squareup.okhttp.Connection.makeTunnel(Connection.java:399)
at com.squareup.okhttp.Connection.upgradeToTls(Connection.java:229)
at com.squareup.okhttp.Connection.connect(Connection.java:159)
at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:175)
at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:120)
at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:330)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:319)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:241)
at com.squareup.okhttp.Call.getResponse(Call.java:271)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:228)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:199)
at com.squareup.okhttp.Call.execute(Call.java:79)
at retrofit.client.OkClient.execute(OkClient.java:53)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:326)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:913)
at $Proxy1.replyTransaction(Unknown Source)
< App Specific Trace >
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable.unsafeSubscribe(Observable.java:7304)
at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
D/Retrofit: ---- END ERROR
retrofit.RetrofitError
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:395)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:913)
at $Proxy1.replyTransaction(Unknown Source)
< App Specific Trace >
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable.unsafeSubscribe(Observable.java:7304)
at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.io.InterruptedIOException
at okio.Timeout.throwIfReached(Timeout.java:146)
at okio.Okio$1.write(Okio.java:75)
at okio.AsyncTimeout$1.write(AsyncTimeout.java:155)
at okio.RealBufferedSink.flush(RealBufferedSink.java:201)
at com.squareup.okhttp.internal.http.HttpConnection.flush(HttpConnection.java:140)
at com.squareup.okhttp.Connection.makeTunnel(Connection.java:399)
at com.squareup.okhttp.Connection.upgradeToTls(Connection.java:229)
at com.squareup.okhttp.Connection.connect(Connection.java:159)
at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:175)
at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:120)
at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:330)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:319)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:241)
at com.squareup.okhttp.Call.getResponse(Call.java:271)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:228)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:199)
at com.squareup.okhttp.Call.execute(Call.java:79)
at retrofit.client.OkClient.execute(OkClient.java:53)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:326)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:913)
at $Proxy1.replyTransaction(Unknown Source)
< App Specific Trace >
at rx.Observable$1.call(Observable.java:145)
at rx.Observable$1.call(Observable.java:137)
at rx.Observable.unsafeSubscribe(Observable.java:7304)
at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:47)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:457)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
My code for this is as follows:
Network call:
return Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
try {
subscriber.onNext(/* Sync network call here. */);
} catch (Exception e) {
subscriber.onError(e);
}
subscriber.onCompleted();
}
})
.onBackpressureBuffer()
// singleton for Schedulers.io()
.subscribeOn(ioScheduler)
// singleton for AndroidSchedulers.mainThread()
.observeOn(mainThreadScheduler);
Which is called like so:
subscription = makeNetworkCall()
.subscribe(new Observer<Object>() {
#Override public void onNext(Object object) {
// close and finish activity
}
#Override public void onError(Throwable e) {
// whoops!
}
});
and is unsubscribed from in the Activity like so:
#Override
protected void onPause() {
super.onPause();
if (subscription != null) {
subscription.unsubscribe();
subscription = null;
if (condition) {
finish();
}
}
}
My understanding of this is that I am getting an InterruptedException because I am unsubscribe()ing from the Subscription in onPause(). We do this for other requests though so I am not sure why I am only seeing it here and why exactly it is happening.
To provide a bit more info on this, the network call happens from an Activity that is launched from the lock screen via a notification action to force the user to unlock their device in order to take action.
My question here is why is this happening and is there a good way to remediate this? According to a few articles, onPause() is where you should unsubscribe from Observables.
More info on this can be see In okHTTP's github issues
Thanks!
This is a guess, but it may be that you are not checking if the subscriber is still there before emitting the interrupted exception.
Try this:
return Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
try {
subscriber.onNext(/* Sync network call here. */);
subscriber.onCompleted();
} catch (Exception e) {
if (!subscriber.isUnsubscribed()) { // <-- check before emitting error
subscriber.onError(e);
}
}
}
})
Upon unsubscription the chain need not do any more work as we don't care about the output anymore. We could just let the current operation complete, and stop the work cleanly when we reach the next operator, but why waste any resources we don't have to? If there's an ongoing network request we want to cancel it. We do this by interrupting the thread. If/when the code on this thread checks if it is interrupted then no more work need be done, and InterruptedIOException can be thrown.
Depending on exactly when unsubscribe is called you may or may not see the error. If unsubscribe is called well before the request has started loading or after it has finished then you may not see this error.
So the reason for this Exception is OkHttp wants to give up as soon as it knows you're not interested in its results and RxJava doesn't want to swallow it in-case it was important for you.
You can add a default error handler to suppress undeliverable InterruptedExceptions and InterruptedIOExceptions which are rarely useful. See RxJava 2 Error Handling, I believe RxJava 1 has a similar construct, but you should consider migrating to RxJava 2. It's really not much of a departure, and RxBinding has been updated to RxJava 2.
onPause() is not necessarily where you should unsubscribe. You should unsubscribe where the logic of your app demands it and where it will prevent memory leaks.
try this:
return Observable.create(e -> {
try {
e.onNext(...);
} catch (Exception ex) {
e.tryOnError(ex);
}
e.onComplete();
});
tryOnError() checks is the Observer is disposed or not when sending error.
reference: https://github.com/ReactiveX/RxJava/issues/4863
I'm working on a large codebase with a lot of network calls. It currently works with Retrofit 1.9 and RxJava 1.x. But, we're trying to go to RxJava 2.x.
I currently keep getting a NetworkOnMainThreadException on our RxJava 2 code. I'm using RxJava 2.x and Retrofit 1.9. The plan is to go to Retrofit 2.x later. But, for now, we need to make this work with RxJava 2 + Retrofit 1.9.
The code in question is:
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<Void>() {
#Override
public void onNext(Void response) {
if (listener != null) {
listener.onSuccess(response);
}
}
#Override
public void onError(Throwable e) {
if (listener != null) {
listener.onError(e);
}
}
#Override
public void onComplete() {
//do nothing for now
}
});
Any ideas how the network operation is ending up on the UI thread when I told it to subscribeOn(Schedulers.io())?
Edit: by request here's the stacktrace. I genericized filenames and such because I'm not allowed to divulge those details...
retrofit.RetrofitError
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:400)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:913)
at <package-name>.network.api.$Proxy40.someApiMethod(Unknown Source)
at <package-name>.SomeApi.someApiMethod(SomeApi.java:30)
Caused by: android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1425)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:102)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:90)
at java.net.InetAddress.getAllByName(InetAddress.java:787)
at com.squareup.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:224)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:193)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:113)
at com.squareup.okhttp.internal.http.HttpEngine.createNextConnection(HttpEngine.java:344)
at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:329)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:319)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:241)
at com.squareup.okhttp.Call.getResponse(Call.java:271)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:228)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:199)
at com.squareup.okhttp.Call.execute(Call.java:79)
at retrofit.client.OkClient.execute(OkClient.java:53)
I have an app that sometimes get crushed, it has never happened to me, some users have reported it... this is the stack trace that google sends me:
java.lang.NullPointerException:
at com.mal.saul.preciosbitcoinmexico.Fragment.FragmentPrincipal.
realizarCambioPrecio(FragmentPrincipal.java:181)
at com.mal.saul.preciosbitcoinmexico.Fragment.FragmentPrincipal.
access$200(FragmentPrincipal.java:42)
at com.mal.saul.preciosbitcoinmexico.Fragment.FragmentPrincipal$2.
onResponse(FragmentPrincipal.java:160)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.
run(ExecutorCallAdapterFactory.java:68)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5637)
at java.lang.reflect.Method.invoke(Native Method:0)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.
run(ZygoteInit.java:959)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)
i think my error is on the onResponse Method, here it is:
#Override
public void onResponse(Call<BtcValuesResponse> call,
Response<BtcValuesResponse> response) {
BtcValuesResponse btcValuesResponse =
response.body();
if(btcValuesResponse != null){
btcValues =
btcValuesResponse.getBtcValues();
showBtcValues();
}
else {
Toast.makeText(getActivity(), "Bitso No Está Disponible Por
el Momento", Toast.LENGTH_SHORT).show();
}
}
any idea?
You are requesting data by Retrofit asynchronously, after request is completed, you show data by calling showBtcValues().
In a case that your request has not completed yet but users change to other screen. So, when it finish, it still calls showBtcValues, then you will get crash because there is no UI component there.
Please try that to recreate the crash. I think it is the issue here.
If it is, the solution is that you should check the state of Fragment (or Activity) where you call request.
As stated before maybe your there is no UI component there
or
You are just assuming the the response Response<BtcValuesResponse> response is valid, but you should check if the API response is not rate limited, or due connectivity errors, the response was an invalid nonce or something else. You need to verify response status before unwrapping it.
if(response.isSuccessful()){
// Get the body, where the BTCValueResponse is
BtcValuesResponse responseBody = response.body();
btcValuesResponse.getBtcValues();
showBtcValues();
}else{
// Check the error stream
System.out.println(Utils.inputStreamToString(wrappedServiceResponse.errorBody().byteStream()));
}
Many RxJava tutorials with RxTextView.textChanges examples and debounce, use 'live search'. For example: Improving UX with RxJava. So I've implemented this example and I tried to play around:
RxTextView.textChanges(searchView)
.observeOn(Schedulers.io())
.skip(1)
.debounce(DELAY_BEFORE_REQUEST_MS, TimeUnit.MILLISECONDS)
.map(new Func1<CharSequence, String>() {
#Override public String call(CharSequence charSequence) {
return charSequence.toString();
}
})
.switchMap(new Func1<String, Observable<Response>>() {
#Override public Observable<Response> call(String query) {
return retrofitService.search(query);
}
})
.subscribe();
Everything looked good, until I decided to simulate GPRS network type on Android emulator.
First api call was triggered, and when I added next letter to 'searchView', app crashed with InterruptedIOException:
java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling.
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:60)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: rx.exceptions.OnErrorNotImplementedException: thread interrupted
at rx.Observable$27.onError(Observable.java:7923)
at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:159)
at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:120)
at rx.internal.operators.OperatorSubscribeOn$1$1$1.onError(OperatorSubscribeOn.java:71)
at rx.observers.SerializedObserver.onError(SerializedObserver.java:159)
at rx.observers.SerializedSubscriber.onError(SerializedSubscriber.java:79)
at rx.internal.operators.OperatorSwitch$SwitchSubscriber.error(OperatorSwitch.java:223)
at rx.internal.operators.OperatorSwitch$InnerSubscriber.onError(OperatorSwitch.java:282)
at rx.internal.operators.OperatorMerge$MergeSubscriber.reportError(OperatorMerge.java:240)
at rx.internal.operators.OperatorMerge$MergeSubscriber.checkTerminate(OperatorMerge.java:776)
at rx.internal.operators.OperatorMerge$MergeSubscriber.emitLoop(OperatorMerge.java:537)
at rx.internal.operators.OperatorMerge$MergeSubscriber.emit(OperatorMerge.java:526)
at rx.internal.operators.OperatorMerge$MergeSubscriber.onError(OperatorMerge.java:250)
at rx.internal.operators.OperatorMap$1.onError(OperatorMap.java:48)
at retrofit2.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:114)
at retrofit2.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:88)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable.unsafeSubscribe(Observable.java:8098)
at rx.internal.operators.OperatorSwitch$SwitchSubscriber.onNext(OperatorSwitch.java:105)
at rx.internal.operators.OperatorSwitch$SwitchSubscriber.onNext(OperatorSwitch.java:60)
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:54)
at rx.internal.operators.OperatorDoOnEach$1.onNext(OperatorDoOnEach.java:85)
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:54)
at rx.observers.SerializedObserver.onNext(SerializedObserver.java:95)
at rx.observers.SerializedSubscriber.onNext(SerializedSubscriber.java:95)
at rx.internal.operators.OperatorDebounceWithTime$DebounceState.emit(OperatorDebounceWithTime.java:132)
at rx.internal.operators.OperatorDebounceWithTime$1$1.call(OperatorDebounceWithTime.java:79)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.io.InterruptedIOException: thread interrupted
at okio.Timeout.throwIfReached(Timeout.java:145)
at okio.Okio$2.read(Okio.java:136)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:306)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:300)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:196)
at okhttp3.internal.http.Http1xStream.readResponse(Http1xStream.java:184)
at okhttp3.internal.http.Http1xStream.readResponseHeaders(Http1xStream.java:125)
at okhttp3.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:723)
at okhttp3.internal.http.HttpEngine.access$200(HttpEngine.java:81)
at okhttp3.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:708)
at okhttp3.internal.http.HttpEngine.readResponse(HttpEngine.java:563)
at okhttp3.RealCall.ge
I have searched a little, and it looks like I'm not alone: first and second.
Author of that first question solved this problem this by wrapping retrofit request with try-catch block.
For me it is attempt of covering bad architecture. And I'm looking for cleaner solution.
Is there a way of ignoring first API call result, and starting new one using RxJava? Or I should try to switch over new Retrofit Call API, and try to cancel previous request (and break a reactive approach)?
I have using Retrofit 2 beta 3, with newest Okio and OkHttp.
Well, the error is pretty explicit, you should add onError handling. It could look something like this:
.subscribe(new Observer<Response>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Response response) {
}
});
However, your subscription will be terminated once an error is emitted, but you can avoid this by handling the errors of the API call like this:
.switchMap(new Func1<String, Observable<Response>>() {
#Override public Observable<Response> call(String query) {
return retrofitService.search(query)
.onErrorResumeNext(Observable.<Response>empty());
}
})
I use Retrofit as network library with Rx-Java. I want to make some centralized error checking for most requests and handle errors or pass it to subscriber's onError() if I cannot handle it. How could I do this?
Something like this
projectListObservable
.subscribeOn(Schedulers.newThread())
.timeout(App.NETWORK_TIMEOUT_SEC, TimeUnit.SECONDS)
.retry(App.NETWORK_RETRY_COUNT)
.onError(e -> {
if (e instanseOf HttpError && ((HttpError)e).getCode == 403){
App.getInstance.getNetworkManager.reAuth();
} else {
throw(e);
}})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ProjectListSubscriber());
Also, I should stop retrying in that case, but keep retry if it's a network problem (instanceof IOException).
I think you want to implement too many tihngs with a single observable.
You can have some HttpErrorHandler which will have method
boolean fix(Exception e)
if error handler fixed the exception(renew token etc) return true. You can have differrent error handlers for different cases or one for everything. TokenErrorHandler RetryErrorHandler and then make a chain.
If all error hadlers return false, throw this exception to the up level