AndroidDev
AndroidDev

Reputation: 21237

Suspend Coroutine Hangs

Trying to get a deeper into coroutines. I have a suspendCancellableCoroutine that is supposed to fetch a network response. I can see in Charles that the network call is dispatched and returns successfully. However, my app just hangs on the network request line.

private suspend fun fetchVisualElementsFromServer(clubId: String): VisualElements {
    return suspendCancellableCoroutine { cont ->
        visualElementsService.fetchVisualElementsForClub(clubId)
            .enqueue(object : Callback<ResultVisualElements> {
                override fun onResponse(
                    call: Call<ResultVisualElements>,
                    response: Response<ResultVisualElements>
                ) {
                    if (response.isSuccessful) {
                        response.body()?.let {
                            if (it.result == RESULT_SUCCESS) {
                       saveVisualElementsResponseInSharedPreferences(it.visual_elements)
                                cont.resume (it.visual_elements)
                            } else {
                                cont.cancel()  //edit
                            }
                        } ?: cont.cancel() //edit
                    } else {
                        cont.cancel(IOException("${response.code()}: ${response.errorBody()}"))
                    }
                }
                override fun onFailure(call: Call<ResultVisualElements>, t: Throwable) {
                    Timber.e(t, "visual elements fetch failed")
                    cont.cancel() // edit
                }
            })
    }
}

This where it hangs:

VisualElementsService.kt

fun fetchVisualElementsForClub(clubId: String): Call<ResultVisualElements> {
    return dataFetcherService.getVisualElementsForClub(clubId)
}

What am I missing here? I tried to make the fetchVisualElementsForClub() a suspend function, but that just makes the suspendCancellableCoroutine throw a Suspension functions can only be called within coroutine body error. But I thought that his was within a coroutine body?

Any help appreciated. Thanks.

EDIT

I response to Rene's answer below, I want to add a few things.

You are right, I am missing three cont.cancel() calls. I've modified the OP. Good points.

I have breakpoints all over the suspendCancellableCoroutine such that any possible scenario (success, failure, etc.) will be hit. But that callback never registers.

Wondering if there is something missing in fetchVisualElementsForClub() that is needed to pass the callback up to the suspendCancellableCoroutine. That seems to be where this is hanging.

Upvotes: 0

Views: 1251

Answers (2)

Rene
Rene

Reputation: 6268

You must call cont.resume() or cont.cancel() on every branch in your callback handling. But in your example at least three cases are missing.

  1. If the response is successful but no body is provided, you call nothing.
  2. If the response is successful, the body is not null, but the it.result is not RESULT_SUCCESS you call nothing.
  3. If something goes wrong in onFailure, you call nothing.

As long as neither resume or cancel is invoked, the coroutine will stay suspended, means hangs.

Upvotes: 2

Amir Abbas
Amir Abbas

Reputation: 395

when you use suspend keyword your are telling that function shoud be called inside a coroutine bode, for example:

suspend fun abc(){
   return
}

when you want to call above function you have to call it inside coroutines such as below:

GlobalScope.launch {
  abc()
}

Upvotes: -1

Related Questions