I am getting a Fatal exception while making an API call using co-routine and retrofit in android kotlin. And app crashes with below exception.
E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-7
Process: com.jamhub.barbeque, PID: 18525
java.net.SocketTimeoutException: timeout
at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.java:656)
at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:664)
at okhttp3.internal.http2.Http2Stream.takeHeaders(Http2Stream.java:153)
at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:131)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
Tried adding time out, CoroutineExceptionHandler
val client = OkHttpClient().newBuilder().addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
.newBuilder()
.build()
return chain.proceed(request)
}
}).callTimeout(30, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
retrofitSocial = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
It shouldn't crash if the exception has occurred ideally it should return the request failure.
As per the error crashing the app - in your call logic you're try-catching the wrong thing :) It should be formed like this:
try {
val response = api?.generateOTP(otpRequestBody)
withContext(Dispatchers.Main) {
when (response?.code()) { } }
catch (e: IOException) {
} /* there are many exceptions thrown by Retrofit, all are subclasses of IOException */
Since it's not response?.code() that's throwing exception, but the api?.generateOTP(otpRequestBody).
As for the timeout itself - you might have wrong URL, weak internet connection, you'll need to provide more info for us to find the reason out :)
Or you can try the CoroutineExceptionHandler:
val exceptionHandler = CoroutineExceptionHandler{_ , throwable->
throwable.printStackTrace()
}
//when you make request:
scope.launch(Dispatchers.IO + exceptionHandler ){
}
Related
I'm using okhttp library v.3.14.7 with Retrofit and Moshi to make network requests.
I have a custom interceptor for handling errors:
class ErrorInterceptorProvider #Inject constructor(
private val serverErrorConverter: ServerErrorConverter
) : Provider<Interceptor> {
override fun get(): Interceptor {
return Interceptor { chain ->
val response = chain.proceed(chain.request().newBuilder().build())
when (response.code()) {
in 400..599 -> {
throw serverErrorConverter.makeErrorException(response)
}
}
return#Interceptor response
}
}
}
the serverErrorConverter.makeErrorException function has the following body:
fun makeErrorException(response: Response): ServerException {
val httpCode = response.code()
val requestId = response.headers().get(NetworkConstants.REQUEST_ID_HEADER)
val responseContent = getResponseContent(response)
val exceptionContent = ExceptionContent.createDefault(context)
/rest of lines/
}
and the getResponseContent(response) has this implementation:
private fun getResponseContent(response: Response): String {
return try {
response.body()!!.string()
} catch (_: Exception) {
response.message()
}
}
I'm trying to test how my app reacts on the response with HTTP code 500. I'm using Charles as a proxy and I change the response to my request to this one:
HTTP/1.1 500 Internal server error
Date: Mon, 24 Oct 2022 10:37:05 GMT
Connection: keep-alive
After I execute the response, I expected my app to show error immediately. However, my loading is not finishing and after some time in my getResponseContent function I'm catching an exception:
java.net.SocketTimeoutException: timeout
W/System.err: at okio.SocketAsyncTimeout.newTimeoutException(JvmOkio.kt:146)
W/System.err: at okio.AsyncTimeout.access$newTimeoutException(AsyncTimeout.kt:158)
W/System.err: at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:337)
W/System.err: at okio.RealBufferedSource.read(RealBufferedSource.kt:189)
W/System.err: at okhttp3.internal.http1.Http1ExchangeCodec$AbstractSource.read(Http1ExchangeCodec.java:389)
W/System.err: at okhttp3.internal.http1.Http1ExchangeCodec$UnknownLengthSource.read(Http1ExchangeCodec.java:529)
W/System.err: at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.java:286)
W/System.err: at okio.RealBufferedSource.read(RealBufferedSource.kt:189)
W/System.err: at com.chuckerteam.chucker.internal.support.TeeSource.read(TeeSource.kt:24)
W/System.err: at okio.RealBufferedSource.select(RealBufferedSource.kt:229)
W/System.err: at okhttp3.internal.Util.bomAwareCharset(Util.java:467)
W/System.err: at okhttp3.ResponseBody.string(ResponseBody.java:181)
W/System.err: at ServerErrorConverter.getResponseContent(ServerErrorConverter.kt:156)
The problem is that it takes some time to produce that exception after the response has been received.
I don't understand why calling body()!!.string() method doesn't simple return an empty string?
The only what I've found is that if i add a Content-Length: 0 header to the previous one everything will work fine.
I'm did one app which is consuming one api. I can search dogs by breed using this endpoint:
https://dog.ceo/api/breed/labrador/images where {labrador} is the breed.
The app works fine when there is internet connection or wifi. But when there is no internet, the retrofit library is throwing an exception:
Process: com.example.retrofitrecyclerviewkotlin, PID: 11879
javax.net.ssl.SSLHandshakeException: SSL handshake aborted: ssl=0xa4b28280: I/O error during system call, Connection reset by peer
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:362)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:336)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:300)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:185)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:224)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:108)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:88)
at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:172)
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)
This is my code that where I should fix this problem:
private fun getRetrofit():Retrofit {
return Retrofit.Builder()
.baseUrl("https://dog.ceo/api/breed/")
.addConverterFactory(GsonConverterFactory.create()) //convert the json to DogResponse
.build()
}
private fun searchByRaza(query:String){
CoroutineScope(Dispatchers.IO).launch {
val call = getRetrofit().create(ApiService::class.java).getDogsByRaza("$query/images")
var perros = call.body()
runOnUiThread {
if (call.isSuccessful) {
val images = perros?.images ?: emptyList()
dogImages.clear()
dogImages.addAll(images)
adapter.notifyDataSetChanged()
} else {
showError()
}
}
}
}
I think that the problem is inside of the method getRetrofit() I tried to add one try-catch to handle the exception but I only got erros:
private fun getRetrofit():Retrofit {
try {
return Retrofit.Builder()
.baseUrl("https://dog.ceo/api/breed/")
.addConverterFactory(GsonConverterFactory.create()) //convertir el json a DogResponse
.build()
}catch (e: Exception){
return null
}
}
It says: Null can not be a value of a non-null type Retrofit Any idea to fix this problem guys I will appreciate it.
I would like to show a message to the user alerting about of the lack of internet connection.
thanks so much.
You can use try/catch block in order to catch exception.
CoroutineScope(Dispatchers.IO).launch {
val call = getRetrofit().create(ApiService::class.java).getDogsByRaza("$query/images")
try {
var perros = call.body()
} catch (exc: Exception) {
withContext(Dispatchers.Main) {
Log.d(TAG, "test: "+exc)
//show toast in main thread
}
}
}
I have something similar with another API, Maybe you need to add the .execute() at the end of var perros. Like this:
val call = getRetrofit().create(APIService::class.java).getCharacterByName("$query").execute()
and add a casting
val employees = call.body() as EmployeeResponse
I'm currently using Retrofit 2.3 and RxAndroid for Android as my network communications. Its working fine most of the time. But sometimes, I get a SocketTimeOut exception (I'm assuming due to issues with the internet). I want to be able to handle this case but, putting a try catch around my retrofit calls in my activity doesn't catch this. Likewise, it doesn't go to the OnError method either (I don't see an option for an OnFailure method). The exception, instead, is shown in my RetrofitHelper class, at the return statement of the intercept method. Here is my Retrofit helper class:
public class RetrofitHelper {
/**
* The APICalls communicates with the json api of the API provider.
*/
public APICalls getAPICalls() {
final Retrofit retrofit = createRetrofit();
return retrofit.create(APICalls.class);
}
/**
* This custom client will append the "username=demo" query after every request.
*/
private OkHttpClient createOkHttpClient() {
final OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
final Request original = chain.request();
final HttpUrl originalHttpUrl = original.url();
final HttpUrl url = originalHttpUrl.newBuilder()
.build();
// Request customization: add request headers
final Request.Builder requestBuilder = original.newBuilder()
.url(url);
final Request request = requestBuilder.build();
return chain.proceed(request);
}
});
return httpClient.build();
}
/**
* Creates a pre configured Retrofit instance
*/
private Retrofit createRetrofit() {
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) // Library for parsing json responses
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // Library for easier threading/background processing
.client(createOkHttpClient())
.build();
}
}
And here is my interface for my API calls
public interface APICalls {
#GET("Vehicle/VehiclePositions.json")
Single<ResponseVehiclePosition> getVehiclePositions();
#GET("TripUpdate/TripUpdates.json")
Single<ResponseTripUpdate> getTripUpdates();
}
And here is the log:
2020-06-05 15:43:34.877 10007-10007/com.samramakrishnan.campusbustracker E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.samramakrishnan.campusbustracker, PID: 10007
java.net.SocketTimeoutException: failed to connect to transitdata.cityofmadison.com/204.147.0.120 (port 80) from /10.0.2.16 (port 35902) after 10000ms
at libcore.io.IoBridge.connectErrno(IoBridge.java:191)
at libcore.io.IoBridge.connect(IoBridge.java:135)
at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
at java.net.Socket.connect(Socket.java:621)
at okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.java:63)
at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:223)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:149)
at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:192)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:121)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:100)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at com.samramakrishnan.campusbustracker.restapi.RetrofitHelper$1.intercept(RetrofitHelper.java:55)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:41)
at io.reactivex.Observable.subscribe(Observable.java:10179)
at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:10179)
at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
at io.reactivex.Single.subscribe(Single.java:2558)
at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
at io.reactivex.Scheduler$1.run(Scheduler.java:134)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:59)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:51)
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:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
I think you are able to catch the exception by the BiConsumer in the subscribe method when you call do the GET call.
According to the source file:
public final Disposable subscribe(final Consumer<? super T> onSuccess, final Consumer<? super Throwable> onError)
so I think you can do something like:
compositeDisopsable.add(getAPICalls().getVehiclePositions()
.subscribeOn(...)
...
.subscribe( response -> {
//do what you want to do with the `response`
}, throwable -> {
if(throwable instanceOf SocketTimeoutException){
//handle your exception
});
I am getting this random exception that crashes my app while making network calls with retrofit and am looking for some guidance for resolution
Fatal Exception: java.io.InterruptedIOException: thread interrupted
at okio.Timeout.throwIfReached(Timeout.java:145)
at okio.Okio$1.write(Okio.java:76)
at okio.AsyncTimeout$1.write(AsyncTimeout.java:180)
at okio.RealBufferedSink.flush(RealBufferedSink.java:216)
at okhttp3.internal.http2.Http2Writer.flush(Http2Writer.java:121)
at okhttp3.internal.http2.Http2Connection.newStream(Http2Connection.java:239)
at okhttp3.internal.http2.Http2Connection.newStream(Http2Connection.java:205)
at okhttp3.internal.http2.Http2Codec.writeRequestHeaders(Http2Codec.java:111)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:50)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at com.happycorp.happy.happyapp.util.Network$1.intercept(Network.java:80)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at com.happycorp.android.commondata.net.RetrofitFactory$CustomHttpMetricsLogger.intercept(RetrofitFactory.java:139)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at com.happycorp.android.commondata.net.RetrofitFactory$1.intercept(RetrofitFactory.java:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:212)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
at okhttp3.RealCall.execute(RealCall.java:77)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:41)
at io.reactivex.Observable.subscribe(Observable.java:10179)
at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:10179)
at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
at io.reactivex.Single.subscribe(Single.java:2558)
at io.reactivex.internal.operators.single.SingleToFlowable.subscribeActual(SingleToFlowable.java:37)
at io.reactivex.Flowable.subscribe(Flowable.java:12218)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onNext(FlowableFlatMap.java:156)
at io.reactivex.internal.operators.flowable.FlowableFromIterable$IteratorSubscription.slowPath(FlowableFromIterable.java:238)
at io.reactivex.internal.operators.flowable.FlowableFromIterable$BaseRangeSubscription.request(FlowableFromIterable.java:123)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onSubscribe(FlowableFlatMap.java:110)
at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribe(FlowableFromIterable.java:68)
at io.reactivex.internal.operators.flowable.FlowableFromIterable.subscribeActual(FlowableFromIterable.java:46)
at io.reactivex.Flowable.subscribe(Flowable.java:12218)
at io.reactivex.internal.operators.flowable.FlowableFlatMap.subscribeActual(FlowableFlatMap.java:52)
at io.reactivex.Flowable.subscribe(Flowable.java:12218)
at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:59)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:51)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
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)
This is what the service interface looks like:
#GET("categories")
Single<Category> getCategories();
#GET("categories/{categoryId}")
Single<Category> getCategory(#Path("categoryId") Integer id);
#GET("categories")
Single<Category> getCategoriesByRootCategory(#Query("rootCategoryId") Integer id);
#GET("products")
Single<ProductSearchResult> getProductsBySingleFilter(#Query(SINGLE_FILTER_BASE + "[field]")
String searchCriteria,
#Query(SINGLE_FILTER_BASE + "[conditionType]")
String conditionType,
#Query(SINGLE_FILTER_BASE + "[value]") String value);
Here is the code that creates the okhttp client and the retrofit interface:
public static OkHttpClient getOkHttpClient(#Nullable final File parentCacheDir,
#NonNull final AuthProvider authProvider,
#NonNull final ApiEnvironment apiEnvironment,
#Nullable final Interceptor cacheInterceptor) {
Cache cache = null;
if (parentCacheDir != null) {
File responseCacheDirectory = new File(parentCacheDir, RESPONSE_CACHE_DIRECTORY);
cache = new Cache(responseCacheDirectory, CACHE_SIZE);
}
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(NETWORK_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.readTimeout(NETWORK_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.writeTimeout(NETWORK_REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.cache(cache);
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
builder.addInterceptor(loggingInterceptor);
builder.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder requestBuilder = chain.request().newBuilder();
requestBuilder.header(KEY_CONTENT_TYPE, APPLICATION_JSON);
requestBuilder = addAuthHeaders(requestBuilder, apiEnvironment);
if (apiEnvironment.getDefaultRequestParams() != null) {
requestBuilder = addDefaultParams(requestBuilder, apiEnvironment);
}
return chain.proceed(requestBuilder.build());
}
});
builder.addNetworkInterceptor(new CustomHttpMetricsLogger());
if (cacheInterceptor != null) {
builder.addNetworkInterceptor(cacheInterceptor);
}
return builder.build();
}
public static Retrofit newInstance(#NonNull final AuthProvider authProvider,
#NonNull final ApiEnvironment apiEnvironment,
#Nullable final File parentCacheDir,
#Nullable final Interceptor debugInterceptor) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(getGson()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(getOkHttpClient(parentCacheDir, authProvider, apiEnvironment, debugInterceptor))
.baseUrl(apiEnvironment.getBaseUrl())
.build();
}
And here is the code that is generating the exception:
public static Flowable<ProductSearchResult> getAllItemsForCategories(final HebMagentoApi magentoService,
List<Category> categories) {
return Flowable.fromIterable(categories).flatMap(category ->
magentoService.getProductsBySingleFilter(HebMagentoApi.MAGENTO_CATEGORY_ID,
HebMagentoApi.MAGENTO_FILTER_EQUAL,
String.valueOf(category.getId())).toFlowable());
}
I think this issue is related to this line in the adapter code:
https://github.com/square/retrofit/blob/master/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/BodyObservable.java#L59
The issue was with how we are creating the RxJava2CallAdapterFactory. We were using Dagger 2 to create the okhttp client and as far as I know that graph was constructed on the main thread and so the rxjava2calladapterfactory would use the main thread by default if we were creating flowables / observables by hand using retrofit.
To fix this so that all of the calls to Retrofit using RxJava2 happen on a background thread we create the call adapter in the the following way:
RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io())
IMPORTANT: The other thing to check to keep your app from crashing is to make sure that you are defining onError functions for each of your RxJava operators: flatMap, map, etc
I have an interceptor that handles some authentication things. If the authentication fails it throws and Exception. According to what I could find throwing an exception should result in onFailure getting called where I handle the exception. Unfortunatly this does not happen and the app simply crashes completely. I am sure I must be missing something but I cant seem to figure it out. Hope someone here can help :)
Code and stack trace below:
val client = OkHttpClient.Builder()
// Add interceptor that throws
.addInterceptor { chain ->
throw Exception("test")
}
.build()
retrofit = Retrofit.Builder()
.baseUrl(baseURL)
.client(client)
.build()
api = retrofit.create(ApiInterface::class.java)
// Create api call...
apicall.enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>?, response: retrofit2.Response<T>?) {
// ....
}
override fun onFailure(call: Call<T>?, t: Throwable?) {
// Expect to go here to handle the Exception. But the app crashes
}
})
Stack trace:
E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: com.testapp.test.debug, PID: 28891
java.lang.Error: java.lang.Exception: test
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1168)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.Exception: test
at com.testapp.test.data.networking.RetrofitWebApi$client$1.intercept(RetrofitWebApi.kt:90)
at com.testapp.test.data.networking.RetrofitWebApi$client$1.intercept(RetrofitWebApi.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
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)
OkHttp will only catch the exception that happens in the Interceptor if it's an IOException. You can see the code that does this here, the relevant parts (simplified) are as below:
try {
Response response = getResponseWithInterceptorChain();
} catch (IOException e) {
responseCallback.onFailure(RealCall.this, e);
}
So if you change your code to the following, you'll get a callback in your onFailure method as expected:
val client = OkHttpClient.Builder()
// Add interceptor that throws
.addInterceptor { chain ->
throw IOException("test")
}
.build()
You should never throw exception which is not IOException in OkHttp's Interceptor since it will not handled by OkHttp.
Check below source code from OkHttp's RealCall.java: