JakeWharton/retrofit2-kotlin-coroutines-adapter

Is there any way to catch timeout exception using your coroutines?

Opened this issue · 11 comments

I configured Retrofit like this:

object RetrofitFactory {
    fun makeRetrofitService(): RetrofitDownloadAPIService {
        val okHttpClient = OkHttpClient().newBuilder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .build()
        return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addCallAdapterFactory(CoroutineCallAdapterFactory())
                .client(okHttpClient)
                .build().create(RetrofitDownloadAPIService::class.java)
    }
}

and then tried to catch SocketTimeoutException like this

try {
            val request = retrofitBuildService.service.downloadPDF(codeType, uuid)
            val response = request.await()
            if (response.isSuccessful) {
                if (response.headers().get("Content-Type").equals(PDF_CONTENT_TYPE)) {
                    return writeToFile(response.body()!!)
                } else {
                    val responseText = response.body()!!.string()
                    JSONParser.jsonErrorParser(responseText)

                }
            } else
                throw ResponseException(response.code())
        }catch (ste:SocketTimeoutException){
            throw NetworkException(ste)
        }

but nothing happened
May be I did something wrong, or there are not way to do timeout?
Thank you

I have the same problem :S

You could try to get the job reference of your async task and setup a invokeOnCompletion callback, I haven't used this the library yet but have had similar problems with Differed<T> in the past. So perhaps try this:

async {
val request = retrofitBuildService.service.downloadPDF(codeType, uuid)
            val response = request.await()
            if (response.isSuccessful) {
                if (response.headers().get("Content-Type").equals(PDF_CONTENT_TYPE)) {
                    return writeToFile(response.body()!!)
                } else {
                    val responseText = response.body()!!.string()
                    JSONParser.jsonErrorParser(responseText)

                }
            } else
                throw ResponseException(response.code())
}.invokeOnCompletion {
    it?.printStackTrace()
}

I hope this helps, just a reminder that the invokeOnCompletion function is not executed on the main thread so you'd need to do something like withContext(UI) or withContext(Dispatchers.Main)

Also can't catch SocketTimeoutException...

@JakeWharton
Please explain what the differences are in your implementation.

adapt (call: Call <T>): Deferred <T>

from the following:

override fun adapt(call: Call<T>): Deferred<T> = GlobalScope.async(IO) { call.execute().body()!! }

in addition to tight binding to the IO context. Also, this approach will throw SocketTimeoutException type exceptions into Deferred and can be handled along with errors that the API throws without any additional effort.

@GrachevVladimiw
execute -> Synchronously send the request and return its response
enqueue -> Asynchronously send the request and notify callback of its response

You can't cancel Call when you use execute. If you use enqueue and try cancel Deferred, Call will be canceled, but with execute you can't cancel executing the call.

@vitoksmile Thank you very much!

For tests:
use MockWebServer, throttleBody period must be greater than okhttpclient timeout and you got SocketTimeOutException

I have not been able to resolve this and its making be doubt my use of coroutines. Nothing i have tried has helped catch exceptions(Any) that occur in the interceptor. If anyone has a working solution that would be fantastic.

@badvok666 please read my answer in #3 (comment)

@badvok666 please read my answer in #3 (comment)

We have another problem. Try can't catch timeout exception at all

@KostyaM check my update! I had added catch (se3: SocketTimeoutException)