Giter Site home page Giter Site logo

kotlin-coroutines-retrofit's Introduction

Kotlin Coroutines for Retrofit

CircleCI codecov codebeat badge

This is a small library that provides the Kotlin Coroutines suspending extension Call.await() for Retrofit 2

Based on kotlinx.coroutines implementation.

New version of library (after 1.0.0) support only Kotlin 1.3

Kotlin 1.2 and experimental coroutines are not supported anymore, but you can use version 0.13.0 for old projects.

Download

Download the JAR:

Gradle:

implementation 'ru.gildor.coroutines:kotlin-coroutines-retrofit:1.1.0'

Maven:

<dependency>
  <groupId>ru.gildor.coroutines</groupId>
  <artifactId>kotlin-coroutines-retrofit</artifactId>
  <version>1.1.0</version>
</dependency>

How to use

NOTE: All examples in this README use runBlocking to build coroutine but it is only useful for testing or examples.

For a real application you probably want to use some other coroutines builder that does not block a thread, for example launch from kotlinx.coroutines.

If you want to use this library for UI please also check the Guide to UI programming with coroutines

There are three suspending extensions:

.await()

Common await API that returns a result or throws an exception

fun Call<T>.await(): T

In case of an HTTP error or an invocation exception await() throws an exception

// You can use retrofit suspended extension inside any coroutine block
fun main(args: Array<String>): Unit = runBlocking {
    try {
        // Wait (suspend) for result
        val user: User = api.getUser("username").await()
        // Now we can work with result object
        println("User ${user.name} loaded")
    } catch (e: HttpException) {
        // Catch http errors
        println("exception${e.code()}", e)
    } catch (e: Throwable) {
        // All other exceptions (non-http)
        println("Something broken", e)
    }
}

.awaitResponse()

Common await API that returns a Response or throws an exception

fun Call<T>.awaitResponse(): Response<T>

In case of an invocation exception awaitResponse() throws an exception

// You can use retrofit suspended extension inside any coroutine block
fun main(args: Array<String>): Unit = runBlocking {
    try {
        // Wait (suspend) for response
        val response: Response<User> = api.getUser("username").awaitResponse()
        if (response.isSuccessful()) {
          // Now we can work with response object
          println("User ${response.body().name} loaded")
        }
    } catch (e: Throwable) {
        // All other exceptions (non-http)
        println("Something broken", e)
    }
}

.awaitResult()

API based on sealed class Result:

fun Call<T>.awaitResult(): Result<T>
fun main(args: Array<String>): Unit = runBlocking {
    // Wait (suspend) for Result
    val result: Result<User> = api.getUser("username").awaitResult()
    // Check result type
    when (result) {
        //Successful HTTP result
        is Result.Ok -> saveToDb(result.value)
        // Any HTTP error
        is Result.Error -> log("HTTP error with code ${result.error.code()}", result.error)
        // Exception while request invocation
        is Result.Exception -> log("Something broken", e)
    }
}

Also, Result has a few handy extension functions that allow to avoid when block matching:

fun main(args: Array<String>): Unit = runBlocking {
    val result: User = api.getUser("username").awaitResult()
    
    //Return value for success or null for any http error or exception
    result.getOrNull()
    
    //Return result or default value
    result.getOrDefault(User("empty-user"))
    
    //Return value or throw exception (HttpException or original exception)
    result.getOrThrow()
    //Also supports custom exceptions to override original ones
    result.getOrThrow(IlleagalStateException("User request failed"))
}

All Result classes also implemented one or both interfaces: ResponseResult and ErrorResult You can use them for access to shared properties of different classes from Result

fun main(args: Array<String>): Unit = runBlocking {
  val result: User = api.getUser("username").awaitResult()
  
  //Result.Ok and Result.Error both implement ResponseResult
  if (result is ResponseResult) {
      //And after smart cast you now have an access to okhttp3 Response property of result
      println("Result ${result.response.code()}: ${result.response.message()}")
  }
  
  //Result.Error and Result.Exception implement ErrorResult
  if (result is ErrorResult) {
      // Here yoy have an access to `exception` property of result
      throw result.exception
  }
}

Nullable body

To prevent unexpected behavior with a nullable body of response Call<Body?> extensions .await() and .awaitResult() are available only for non-nullable Call<Body> or platform Call<Body!> body types:

fun main(args: Array<String>): Unit = runBlocking {
  val user: Call<User> = api.getUser("username")
  val userOrNull: Call<User?> = api.getUserOrNull("username")
  
  // Doesn't work, because User is nullable
  // userOrNull.await()
    
  // Works for non-nullable type
  try {
      val result: User = user.await()  
  } catch (e: NullPointerException) {
      // If body will be null you will get NullPointerException
  }
  
  // You can use .awaitResult() to catch possible problems with nullable body
  val nullableResult = api.getUser("username").awaitResult().getOrNull()
  // But type of body should be non-nullable
  // api.getUserOrNull("username").awaitResult()
  
  // If you still want to use nullable body to clarify your api
  // use awaitResponse() instead:
  val responseBody: User? = userOrNull.awaitResponse().body()
}

Parallel requests

By wrapping call with kotlinx.coroutines async(), you may run a few requests parallelly without waiting for the previous request.

fun main(args: Array<String>): Unit = runBlocking {
  val users = listOf("user1", "user2", "user3")
      .map { username ->
        // Pass any coroutine context that fits better for your case
        // Coroutine Dispatcher also controls parallelism level 
        // for CommonPool parallelism is `availableProcessors - 1`
        // But you can use any custom dispatcher with any parallelism strategy
        async(CommonPool) {
            // Send request. We use `awaitResult()` here to avoid try/catch, 
            // but you can use `await()` and catch exceptions
            api.getUser(username).awaitResult() 
        }
      }
      // Handle results
      // in this example we get result or null in case of error and filter all nulls
      .mapNotNull {
        // Wait (suspend) for result of `async()` and get result of request
        // We must call first `await()` only when all `async` blocks are created for parallel requests
        it.await().getOrNull()
      }
}

You can read more about concurrent usage of async in the kotlinx.coroutines guide

kotlin-coroutines-retrofit's People

Contributors

bohsen avatar elizarov avatar erikhuizinga avatar fengdai avatar gildor avatar jakewharton avatar johnjohndoe avatar kelvin2468 avatar kingsleyadio avatar louiscad avatar paulwoitaschek avatar subhrajyotisen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kotlin-coroutines-retrofit's Issues

0.8.1 fails to resolve in Gradle where 0.7.1 has no issues

For some reason when upgrading to 0.8.1, gradle cannot resolve the JAR from the server.

Error:Unable to resolve dependency for ':app@productionDebug/compileClasspath': Could not resolve ru.gildor.coroutines:kotlin-coroutines-retrofit:0.8.1.
Open File
Show Details

Unable to invoke no-args constructor for retrofit2.Call

Version : retrofit 2.6.2
Json: GsonConverterFactory

When I use Call.awaitResponse() Call.await() hava error
but I use Call.enqueue() Call.execute() no problem

java.lang.RuntimeException: Unable to invoke no-args constructor for retrofit2.Call. Registering an InstanceCreator with Gson for this type may fix this problem.
        at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:228)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:121)
        at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138)
        at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:784)
 Caused by: java.lang.UnsupportedOperationException: Interface can't be instantiated! Interface name: retrofit2.Call
        at com.google.gson.internal.UnsafeAllocator.assertInstantiable(UnsafeAllocator.java:117)
        at com.google.gson.internal.UnsafeAllocator$1.newInstance(UnsafeAllocator.java:49)
        at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:225)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212) 
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39) 
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27) 
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225) 
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:121) 
        at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138) 
        at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:784) 

No Response When Call a Service

I use awaitResult method

public fun asyncCreateNewTask(dayPlanTask: DayPlanTask) = runBlocking {
        var state: Status = Status.Error

        val service = InspectionAPI().getInspectionService()

        val result : Result<Reply.Single<DayPlanTask>> = service.createWeeklyPlanTask(dayPlanTask).awaitResult()
        when (result) {
            is Result.Ok -> {
                state = Status.OK
            }
            is Result.Error -> {
                state = Status.InternalServerError
            }
            is Result.Exception -> {
                state = Status.Error
            }
            else -> {
                Log.e("WAIT_TRY", "ELSE branch")
            }
        }

        Log.e("STATE", state.toString())
    }

the app is stuck and not responsible, also it's not throwing any exception
it isn't entered in onResponse and onFailure functions at CallAwait.awaitResult()

Handling Call<Unit>

Hello, I have a question.
Some my APIs return "204: NO CONTENT" so I handle it by Retrofit like:

fun myApi(): Call<Unit>

In this case, myApi().await() throws NPE since the response body is always null.
How to solve the problem?
Thanks.

Call cancellation feedback causes crash when coroutine is cancelled

Hello.

I've been experiencing a couple of crashes with this library. Spent a lot of time trying to figure out why.
I discovered it was due to this code in Callback#onFailure

//if (call.isCanceled) return
/**
 * It is better just to invoke resumeWithException:
 * 1. If the coroutine was cancelled, then resumeWithException is just ignored
 * 2. However, if somebody just invokes Call.cancel(), then we should resume
 *    continuation that was doing `await` on this call, or it'll await forever.
  */
continuation.resumeWithException(t)

Basically, if the coroutine is already canceled, and then continuation.resumeWithException(e) is called, everything blows up because this exception is different from the one originally held by the coroutine.
A comment from AbstractCoroutine#resumeWithException (from kotlinx.coroutine library):

// ignore resumes on cancelled continuation, but handle exception if a different one is here

Fixing this is involves simply checking whether the coroutine has been cancelled before attempting to resume with exception

override fun onFailure(call: Call<T>, t: Throwable) {
    if (!cont.isCancelled) cont.resumeWithException(t)
}

This is still in line with the 2 comments @elizarov made regarding the resumption:

  1. If the coroutine was canceled, we can as well avoid resuming it anymore
  2. We still correctly resume continuation if someone invokes Call#cancel directly

Result.OK value = null

when i using .awaitResult(),

i always get result = Result.Ok{value=null null null, response=Response{protocol=http/1.1, code=200, message=OK, url = http://xxxxx)

I don't know why the value is null.
I am using 0.13.0-eap13 version and retrofit 2.5.0 now.

before update, it has value when i am using 0.9.0 version. please help

No response coming back

I'm trying to get a token but nothing is happening. Here is my code:

fun handleLogin() {
        println("about to get token")
        launch {
            print("getting token")
            val token = service.getToken(code = code, verifier = verifier).await()
            print("token is $token")
        }
}

It prints out the "about to get token" but never prints out the "getting token" nor the "token is $token". I know it's not Retrofit because it works when I use the basic enqueue functionality. Any ideas?

Incompatibility with kotlinx.coroutines 0.16

Hi,

When using this library with kotlinx.coroutines version 0.16, I get the following error:

java.lang.NoSuchMethodError: No direct method <init>(Lkotlin/coroutines/experimental/Continuation;Z)V in class Lkotlinx/coroutines/experimental/CancellableContinuationImpl; or its super classes (declaration of 'kotlinx.coroutines.experimental.CancellableContinuationImpl' appears in /data/app/my.app.packagename.debug-2/base.apk)
                                                                                             at ru.gildor.coroutines.retrofit.CallAwaitKt.awaitResult(CallAwait.kt:124)

The issue may be present with version 0.15 too

How to use this library ?

Hi,
I am trying to use this library .Here is my code :-

@GET(ApiEndPoint.API_MODELS)
Call<ModelResponse> getCarModelsAsync2(@Query("itemsPerPage") String itemsPerPage);
fun getModelData() = runBlocking {
        try {
            // Wait (suspend) for response
            val result: Result<ModelResponse> = dataManager.getCarModelsAsync2("100").awaitResult()
            // Check result type
            when (result) {
                //Successful HTTP result
                is Result.Ok -> {

                }
                // Any HTTP error
                is Result.Error -> {
//                    log("HTTP error with code ${result.error.code()}", result.error)
                }
                // Exception while request invocation
                is Result.Exception -> {
//                    log("Something broken", e)
                }
            }
//            if (response) {
//                // Now we can work with response object
//                println("User ${response.body().toString()} loaded")
//            }
        } catch (e: Throwable) {
            // All other exceptions (non-http)
            e.printStackTrace()
        }
    }
public suspend fun <T : Any> Call<T>.awaitResult(): Result<T> {
    return suspendCancellableCoroutine { continuation ->
        enqueue(object : Callback<T> {
            override fun onResponse(call: Call<T>?, response: Response<T>) {
                continuation.resumeWith(runCatching {
                    if (response.isSuccessful) {
                        val body = response.body()
                        if (body == null) {
                            Result.Exception(NullPointerException("Response body is null"))
                        } else {
                            Result.Ok(body, response.raw())
                        }
                    } else {
                        Result.Error(HttpException(response), response.raw())
                    }
                })
            }

            override fun onFailure(call: Call<T>, t: Throwable) {
                // Don't bother with resuming the continuation if it is already cancelled.
                if (continuation.isCancelled) return
                continuation.resume(Result.Exception(t))
            }
        })

        registerOnCompletion(continuation)
    }
}

I can see the response in log but nowhere callback is coming. I have attached debug everywhere

Catch an exception that has been thrown inside Retrofit Interceptor?

Inside my retrofit interceptor I am throwing a RespException (A custom RuntimeException) like this:

val interceptor = Interceptor {
    chain ->

    val request = chain
            .request()
            .newBuilder()
            .build()

    // <other irrelevant things here>

    var resp: Response

    try {

        resp = chain.proceed(request)

    } catch (e: Exception) {
        val errorTxt = "Unexpected error"

        // <other irrelevant things here>

        var re = RespException(errorTxt)
        re.initCause(e)
        throw re
    }

    // <other irrelevant things here>

    resp
}

But when I try to catch it like this:

fun load() = async(UI) {
    // <other irrelevant things here>

    try {
        var resp = Api.resources.listPrincipal(null, null, null, null, null).await()

        // <other irrelevant things here>

    } catch (e: RespException){
        // it's not called
    } catch (t: Throwable){
        // it's not called
    }

    // <other irrelevant things here>
}

It doesn't work. It crashes my app with this stacktrace (I turned off the server purpose, just to get the error):

E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: br.com.martinlabs.usecase, PID: 9587
com.simpli.model.RespException: Unexpected Error
    at br.com.martinlabs.usecase.service.Api$Companion$interceptor$1.intercept(Api.kt:51)
    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:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    at java.lang.Thread.run(Thread.java:818)
 Caused by: java.net.ConnectException: Failed to connect to /192.168.30.194:8080
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:225)
    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 br.com.martinlabs.usecase.service.Api$Companion$interceptor$1.intercept(Api.kt:45)
    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:1113) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
    at java.lang.Thread.run(Thread.java:818) 
 Caused by: java.net.ConnectException: failed to connect to /192.168.30.194 (port 8080) after 10000ms: isConnected failed: ECONNREFUSED (Connection refused)
    at libcore.io.IoBridge.isConnected(IoBridge.java:234)
    at libcore.io.IoBridge.connectErrno(IoBridge.java:171)
    at libcore.io.IoBridge.connect(IoBridge.java:122)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:183)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:452)
    at java.net.Socket.connect(Socket.java:884)
    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 br.com.martinlabs.usecase.service.Api$Companion$interceptor$1.intercept(Api.kt:45) 
    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:1113) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
    at java.lang.Thread.run(Thread.java:818) 
 Caused by: android.system.ErrnoException: isConnected failed: ECONNREFUSED (Connection refused)
    at libcore.io.IoBridge.isConnected(IoBridge.java:223)
    at libcore.io.IoBridge.connectErrno(IoBridge.java:171) 
    at libcore.io.IoBridge.connect(IoBridge.java:122) 
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:183) 
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:452) 
    at java.net.Socket.connect(Socket.java:884) 
    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 br.com.martinlabs.usecase.service.Api$Companion$interceptor$1.intercept(Api.kt:45) 
    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:1113) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
    at java.lang.Thread.run(Thread.java:818) 

fun okhttp3.Call.await() = TODO() ?

Hello

I just gave a try to your library and found it very handy.

I often have to mix retofit and okhttp3 calls inside the same project and I was thinking it would make a lot of sense to have those await extension functions for okhttp as well.

Since retrofit requires okhttp3, that would not add a new dependancy for the users of the library

A use case would be

val retrofitData = api.getData().await()
val okRequest = createRequest(retrofitData.url)
val okData = okClient.newCall(okRequest).await()

Checking for continuation.isCancelled only in failures

I have just noticed that the methods in CallAwait.kt only check if the continuation.isCancelled on the onFailure callback. Is there any reason why onResponse doesn't implement the same behavior ?

override fun onFailure(call: Call<T>, t: Throwable) {
    // Don't bother with resuming the continuation if it is already cancelled.
    if (continuation.isCancelled) return
    ...
}

No documentation / source code

I see no documentation nor sources in Android Studio (3.2.1), just the compiled .class files. Maybe the wrong artifacts are being uploaded to whatever online repo?

I looked into the Gradle cache (where the JARs are stored) and it does include the sources.jar file, but it is empty:

$ jar -tf ~/.gradle/caches/modules-2/files-2.1/ru.gildor.coroutines/kotlin-coroutines-retrofit/1.0.0/3380d25eb4f4e54022fbe59ba7261a9d05780177/kotlin-coroutines-retrofit-1.0.0-sources.jar

META-INF/
META-INF/MANIFEST.MF
ru/
ru/gildor/
ru/gildor/coroutines/
ru/gildor/coroutines/retrofit/

These are all the files in the JAR: just directories, no files.

It seems there's something wrong with your source artifact packaging!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.