What is the cause of this NetworkOnMainThreadException - android

Before you hover over the close button. Please fully read this question.
Preface
Obviously, this exception is caused by running a network operation on the Main Thread. However, as you can see from the stacktrace, this error originates within doInBackground of an AsyncTask! I also do not transition onto the Main Thread after that point.
Interestingly, I cannot replicate this issue on my devices. It is also not an issue that crops up for the vast majority of my users. Therefore, it cannot be a general implementation error.
Steps
Main Thread
Something causes the channel list to refresh (be it manual or initial load)
My API library is called (appendix 1)
Background Thread
The AsyncTask calls the API library to get the body of a URL (appendix 2)
My Cache library checks whether the body has already been saved, it does not change the Thread.
The API library calls the necessary OkHttp methods to pull the body of the URL (appendix 3)
Stacktrace
Fatal Exception: java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:309)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
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 android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1273)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:249)
at libcore.io.IoBridge.recvfrom(IoBridge.java:549)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:481)
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:37)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237)
at okio.Okio$2.read(SourceFile:139)
at okio.AsyncTimeout$2.read(SourceFile:211)
at okio.RealBufferedSource.indexOf(SourceFile:306)
at okio.RealBufferedSource.indexOf(SourceFile:300)
at okio.RealBufferedSource.readUtf8LineStrict(SourceFile:196)
at okhttp3.internal.http.Http1xStream.readResponse(SourceFile:184)
at okhttp3.internal.http.Http1xStream.readResponseHeaders(SourceFile:125)
at okhttp3.internal.http.HttpEngine.readNetworkResponse(SourceFile:723)
at okhttp3.internal.http.HttpEngine.access$200(SourceFile:81)
at okhttp3.internal.http.HttpEngine$NetworkInterceptorChain.proceed(SourceFile:708)
at okhttp3.internal.http.HttpEngine.readResponse(SourceFile:563)
at okhttp3.RealCall.getResponse(SourceFile:241)
at okhttp3.RealCall$ApplicationInterceptorChain.proceed(SourceFile:198)
at okhttp3.RealCall.getResponseWithInterceptorChain(SourceFile:160)
at okhttp3.RealCall.execute(SourceFile:57)
at com.mypackagename.app.http.Api.getBody(SourceFile:1472)
at com.mypackagename.app.tasks.GetChannelListTask$2.renewCache(SourceFile:72)
at com.mypackagename.app.utils.Cache.get(SourceFile:27)
at com.mypackagename.app.tasks.GetChannelListTask.doInBackground(SourceFile:69)
at com.mypackagename.app.tasks.GetChannelListTask.doInBackground(SourceFile:24)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
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)
Appendix 1
public void getChannelList(final IGetChannelListCallback callback) {
try {
GetChannelListTask task = new GetChannelListTask(mContext.get(), callback);
AsyncTaskCompat.executeParallel(task);
} catch (RejectedExecutionException e) {
Crashlytics.logException(e);
}
}
Appendix 2
protected ArrayList<Channel> doInBackground(String... urls) {
final ArrayList<Channel> channelList = new ArrayList<Channel>();
// ...
String json = Cache.get(context, GET_CHANNELS_CACHE, GET_CHANNELS_CACHE_TIME, new IBadCache() {
public String renewCache() {
return Api.getInstance(context).getBody(GET_CHANNELS_URL);
}
});
// ...
return channelList;
}
Appendix 3
#WorkerThread
#NonNull
public String getBody(#NonNull final String url) {
try {
Request request = new Request.Builder()
.url(logUrl(url))
.build();
Response response = mClient.newCall(request).execute();
return response.body().string();
} catch (IOException e) {
Utils.log("API", "E::"+e.toString());
} catch (SecurityException e) {
reportNoInternetPermission();
}
return "";
}
Update 1
Checking the Thread resulted in the expected:
refreshChannelList: MainThread: true
getChannelList: MainThread: true
onPreExecute: MainThread: true
doInBackground: MainThread: false
renewCache: MainThread: false
getBody: MainThread: false
onPostExecute: MainThread: true
Moving to a background Thread upon doInBackground and continuing through to the OkHttp call, returning to the Main Thread once at onPostExecute.

Hm, looks like your problem in in case when your do something(calling some operation with context usage), in doInbackground method. If your context instance of Activity/Fragment etc. (taking from UI component) thats mean that operation which used your context use MainThread for executing. thats why u can get such kind of exception.
Try execute this
String json = Cache.get(context, GET_CHANNELS_CACHE, GET_CHANNELS_CACHE_TIME, new IBadCache() {
public String renewCache() {
return Api.getInstance(context).getBody(GET_CHANNELS_URL);
}
});
on
#Override
protected void onProgressUpdate(Object... values) {
super.onProgressUpdate(values);
}
with publishProgress(Object obj); on doInbackground method. It must help

Related

Retrofit/OkHTTP/RxJava intermittent InterruptedIOException

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

ConcurrentModificationException with ORMlite on Android

I'm having tons of ConcurrentModificationException when doing some update operations with my database using ORMlite. I'm wrapping the code that does the actual update into rx.Observables to make it asynchronously. Looks like this:
#Override
public void setFavoriteTeams(List<Team> teams) {
final Iterator<Team> it = teams.iterator();
Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(final Subscriber<? super Object> subscriber) {
try {
List<String> teamIds = new ArrayList<>();
while (it.hasNext()) {
Team team = it.next();
teamIds.add(team.getmSipId());
}
setFavoriteTeamsSql(teamIds);
} catch (Exception e) {
handleException(e);
} finally {
subscriber.onNext(null);
subscriber.onCompleted();
}
}
}).subscribeOn(Schedulers.computation()).subscribe();
}
Ironically, a while ago I started using Interator instead of a for-loop to avoid any ConcurrenceModificationException that may happen, but instead they increased a lot. The setFavoriteTeamsSql(List<String>) method simply uses ORMlite's UpdateBuilder class to update the table. The exception is being thrown at the Team team = it.next(); line.
Any idea about how to fix this? Right now I'm rolling back the changes and going back to the for-loop. But I'd like doing this the right way.
UPDATE:
This is how the Stacktrace looks like:
Non-fatal Exception: java.util.ConcurrentModificationException
at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
at com.siplay.android_siplay.data.cache.db.DBTeamCache$4.call(DBTeamCache.java:164)
at com.siplay.android_siplay.data.cache.db.DBTeamCache$4.call(DBTeamCache.java:157)
at rx.Observable.unsafeSubscribe(Observable.java:10150)
at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
at rx.internal.schedulers.EventLoopsScheduler$EventLoopWorker$1.call(EventLoopsScheduler.java:172)
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.access$201(ScheduledThreadPoolExecutor.java:154)
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)
UPDATE 2:
...
mNetworkTeamRepository
.subscribeOn(Schedulers.io())
.obseveOn(AndroidSchedulers.mainThread())
.subscribe(new DefaultSubscriber<List<Teams>>() {
#Override
public void onSuccess(List<Team> teams) {
mTeamsCache.setFavoriteTeams(teams);
}
public void onResult(List<Team> teams) {
callback.showTeams(teams);
}
});
...
The TeamRepository internally uses a Retrofit service to get the server-side response.
Your code is equivalent to
public void setFavoriteTeams(List<Team> teams) {
Observable
.fromIterable(teams)
.map(Team::getmSipId)
.toList()
.doOnNext(this::setFavoriteTeamsSql)
.subscribeOn(Schedulers.computation())
.subscribe();
}
But I still can't see how your code emits ConcurrentModificationException - maybe a stack trace will help?

RxTextView text changes + retrofit call leads to InterruptedIOException

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());
}
})

Retrofit custom error handler + sync & asyc requests

I have a custom error handler that checks RetrofitError it gets passed and rethrows it as custom exceptions
private static ErrorHandler getErrorHandler() {
return new ErrorHandler() {
#Override
public Throwable handleError(RetrofitError cause) {
switch (cause.getKind()) {
case NETWORK: return new NetworkException(cause);
case HTTP: return new ApiException(cause);
default: return cause;
}
}
};
}
If this is my endpoint
#GET(USERS_GET_URL)
User getUsers() throws NetworkException, ApiException;
while executing synchronous request I try...catch and handle each custom exception as I want. When it is done asynchronously using
#GET(USERS_GET_URL)
void getUsers(Callback<User> cb) throws NetworkException, ApiException;
the handled exception gets rethrown as RetrofitError. The following snippet of code is from CallbackRunnable class of Retrofit which executes the request
try {
final ResponseWrapper wrapper = obtainResponse();
callbackExecutor.execute(new Runnable() {
#Override public void run() {
callback.success((T) wrapper.responseBody, wrapper.response);
}
});
} catch (RetrofitError e) {
Throwable cause = errorHandler.handleError(e);
final RetrofitError handled = cause == e ? e : unexpectedError(e.getUrl(), cause);
callbackExecutor.execute(new Runnable() {
#Override public void run() {
callback.failure(handled);
}
});
}
As it can be seen, my custom exceptions are getting rethrown as RetrofitError which makes me loose valuable information. Is there any way I can bypass custom error handling for just the async requests?
In your ErrorHandler you pathing original RetrofitError as cause, so as result in your Callback#failure(RetrofitError error) to get actual information you need to write next code: error.getCause().getCause(). This error will contain response that server send with all the data.
But error handler was created for sync request and after some time square team decided to close this gap this way. For more info you can read: https://gist.github.com/benvium/66bf24e0de80d609dac0
As for me, I don't recommend to use ErrorHander for async way, because I don't find any good solution to handle different types of error. It was much easier to get data right from initial RetrofitError.

Strange behavior in long-running XML parsing method

We have the following method to parse an XML file and display its contents accordingly:
#Background
#AfterInject
void parseXMLAndLoadContent() {
try {
URL url = new URL(URI);
xmlParser.setInput((InputStream) url.getContent());
showProgressDialog();
angeboteXML = xmlParser.parse();
if (angeboteXML != null) { // Breakpoint here
if (angeboteXML.backgroundImage != "") {
loadBitmapFromUrl(angeboteXML.backgroundImage);
}
loadAngebote();
} else
throw new Exception();
} catch (Exception exception) {
showToast();
} finally {
dismissProgressDialog();
}
}
As you can see it is supposed to show the a progress dialog (method annotated with #UiThread), then call the XML parser and handle the result.
Unfortunately, this does only work when I set a breakpoint in the line mentioned above, just after the call of parse(). Somehow, this leads to a certain grace period, that allows the parser to handle the document. If I just let it run without the breakpoint, nothing happens, not even the progress dialogue shows up and angeboteXML is null.
Since I am no Android expert - why is this happening and is there anything I can improve? As far as my understanding goes, this should be executed sequentially, i.e. after the parser is done parsing, everything else is executed. But this is somehow not the case here.
Edit 16/05/13 13:21:
Adding a Thread.sleep(1000) after parse() helps, but it seems more like a workaround than a fix.
This is the trace log of method execution:
05-16 13:19:26.168: I/AngeboteActivity(6051): Entering [void parseXMLAndLoadContent() ]
05-16 13:19:26.278: I/AngeboteXMLParser(6051): Entering [public de.sample.app.helper.AngeboteXML parse() throws org.xmlpull.v1.XmlPullParserException, java.io.IOException]
05-16 13:19:26.318: I/AngeboteXMLParser(6051): Exiting [public de.sample.app.helper.AngeboteXML parse() throws org.xmlpull.v1.XmlPullParserException, java.io.IOException], duration in ms: 35
05-16 13:19:26.318: I/AngeboteActivity(6051): Exiting [void parseXMLAndLoadContent() ], duration in ms: 147
05-16 13:19:26.318: I/AngeboteActivity(6051): Entering [void loadAngebote() ]
loadAngebote() is where a NPE is thrown, since the parser seems to be not yet finished.
Edit 16/05/13 13:29
This is how the parse() method is implemented:
public AngeboteXML parse() throws XmlPullParserException, IOException {
this.xml = new AngeboteXML();
int eventType = this.parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG :
this.readStartTag(this.parser.getName());
break;
case XmlPullParser.END_TAG :
this.readEndTag(this.parser.getName());
break;
case XmlPullParser.TEXT :
this.readText(this.parser.getText());
break;
default :
break;
}
this.parser.next();
eventType = this.parser.getEventType();
}
return xml;
}
Actually, I think this is a design problem. The call to xmlParser.parse() is the task you want to execute in the background. Everything before and after that belongs in another method, which triggers this task.
I don't know how the #Background feature works, but if you would use the http://developer.android.com/reference/android/os/AsyncTask.html, there would be a onProgressUpdate (put showProgressDialog() in here) and a onPostExecute callback (put dismissProgressDialog() in here). Looking at the documentation of android annotations, I would suggest the following:
#AfterViews
void initXMLParsing() {
showProgressDialog();
parseXMLAndLoadContent();
}
#Background
void parseXMLAndLoadContent() {
try {
// ...
} catch (Exception exception) {
showToast();
} finally {
dismissProgressDialog();
}
}

Categories

Resources