JakeWharton/retrofit2-kotlin-coroutines-adapter

okhttp interceptor throw SocketTimeoutException caused the crash

erichyx opened this issue · 7 comments

I use retrofit2 and interface return Deferred object, then I got an SocketTimeoutException using the okhttp interceptor, the whole app crashed.

Before using the coroutine, this SocketTimeoutException will not cause app crash, so I don't know how to deal with this problem.

interface api {
@get("info")
fun request(): Deferred
}

class RequestInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response? {
var request = chain.request()
// do something
return chain.proceed(request)
}
}

when I disconnect the network to request and get a retrofit2.httpexception, the app also crashed.

g_82kn4cs1e0o6ml9koqj60

I have used the safeApiCall function to catch exceptions per network request.

suspend fun safeApiCall(call: suspend () -> Response): Response {
return try {
call()
} catch (e: Exception) {
Response.error(IOException(e))
}
}

I seem to have a problem in the same area. But have no idea as to what exactly the problem is. Here is my stacktrace. I have also opened an issue in the retrofit project, but this might be more appropriate.
Also, I can not reproduce my crash. It only happens sporadically.


com.crashlytics.android.core.CrashlyticsBackgroundWorker.submitAndWait (SourceFile:43)
--
  | com.crashlytics.android.core.CrashlyticsController.handleUncaughtException (SourceFile:285)
  | com.crashlytics.android.core.CrashlyticsController$5.onUncaughtException (SourceFile:269)
  | com.crashlytics.android.core.CrashlyticsUncaughtExceptionHandler.uncaughtException (SourceFile:30)
  | java.lang.ThreadGroup.uncaughtException (ThreadGroup.java:1068)
  | java.lang.ThreadGroup.uncaughtException (ThreadGroup.java:1063)
  | kotlinx.coroutines.CoroutineExceptionHandlerImplKt.handleCoroutineExceptionImpl (SourceFile:35)
  | kotlinx.coroutines.CoroutineExceptionHandlerKt.handleExceptionViaHandler (SourceFile:47)
  | kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException (SourceFile:31)
  | kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException$default (SourceFile:24)
  | kotlinx.coroutines.AbstractContinuation.handleException (SourceFile:254)
  | kotlinx.coroutines.AbstractContinuation.initParentJobInternal$kotlinx_coroutines_core (SourceFile:206)
  | kotlinx.coroutines.AbstractContinuation.initParentJobInternal$kotlinx_coroutines_core (SourceFile:144)
  | kotlinx.coroutines.ResumeAwaitOnCompletion.invoke (SourceFile:1236)
  | kotlinx.coroutines.JobSupport.notifyCompletion (SourceFile:1351)
  | kotlinx.coroutines.JobSupport.tryFinalizeFinishingState (SourceFile:292)
  | kotlinx.coroutines.JobSupport.tryFinalizeFinishingState (SourceFile:225)
  | kotlinx.coroutines.JobSupport.tryFinalizeFinishingState (SourceFile:792)
  | kotlinx.coroutines.JobSupport.makeCompleting$kotlinx_coroutines_core (SourceFile:715)
  | kotlinx.coroutines.CompletableDeferredImpl.completeExceptionally (SourceFile:72)
  | com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory$BodyCallAdapter$adapt$2.onFailure (SourceFile:97)
  | retrofit2.OkHttpCall$1.callFailure (SourceFile:135)
  | retrofit2.OkHttpCall$1.onFailure (SourceFile:130)
  | okhttp3.RealCall$AsyncCall.execute (SourceFile:215)
  | okhttp3.internal.NamedRunnable.run (SourceFile:32)
  | java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1162)

@erichyx @noho13
If you launch your suspend method with Job, change it to SupervisorJob.
Also, catch Throwable instead of Exception.

@vitoksmile It doesn't work, here is my code snippet.

val parentJob = SupervisorJob()
val uiScope = CoroutineScope(Dispatchers.Main + parentJob)

uiScope.launch {
safeApiCall {
api.getXXX(Id).await()
}
}

suspend fun safeApiCall(call: suspend () -> Response): Response {
return try {
call()
} catch (e: Throwable) {
Response.error(e)
}
}

I had the same problem. It seems that upgrading coroutines to version 1.1.1 solved this for me. I had version 1.0.0 before. My code:

private val mJob = SupervisorJob()
private val mScope = CoroutineScope(Dispatchers.Main + mJob)

fun fetchAllData() {
        mScope.launch(Dispatchers.IO) {
            try {
                val fetchResult = configApi.getSettingsAsync()
                val result= fetchResult.await()
                Log.d(TAG, "Received result ${result?.id}")
            } catch (se: SocketTimeoutException) {
                Log.e(TAG, "Error: ${se.message}")
               ...
            } catch (ex: Throwable) {
                Log.e(TAG, "Error ${ex.message}")
               ...
            }
        }
    }

    
override fun onCleared() {
        super.onCleared()
        mScope.coroutineContext.cancelChildren()        
    }

@engbibi That's right, thank you!

Hello, is there any way to catch the exceptions in any other way? This means i have to add try catch for every call. I don't see this very convenience.

Hello, this also happens if you have a CoroutineWorker that calls a Deferred<Response<T>> when the user connection is not stable if crashes when a SocketTimeout is thrown.

Any ideas on this?