2

Code changes applied.

I followed a guide on how to create a kotlin coroutine and do network request and ended up with:

suspend fun <T : Any> safeApiCall(call: suspend () -> Response<T>): ApiResult<T> {
    return safeApiResult(call)
}

private suspend fun <T: Any> safeApiResult(call: suspend ()-> Response<T>) : ApiResult<T>{
    val response = call.invoke()

    return if (response.isSuccessful) {
        val body = response.body()

        if (body == null) {
            ApiResult.Error(response.code())
        } else {
            ApiResult.Success(body)
        }
    } else {
        ApiResult.Error(response.code())
    }
}

suspend fun getSnappedPoints(path: String): ApiResult<SnappedPointsData> {
    return safeApiCall(
        call = { googleRoadsService.getSnappedPoints(path).await()}
    )
}

and calling the network request looks like this:

private fun getSnappedPoints() {
    val paths = Utils.getPathFromLocations(locations)

    CoroutineScope(Dispatchers.IO).launch {
        val results = paths.map {
            async { googleRoadsRepository.getSnappedPoints(it) }
        }.awaitAll()

        Timber.i("results: ${results.size}")

        val snappedPoints = ArrayList<LocationSnap>()

        results.forEach {
            if (it is ApiResult.Success) {
                snappedPoints.addAll(it.data.snappedPoints)
            }
        }

        withContext(Dispatchers.Main) {
            if (snappedPoints.isNotEmpty()) {
                drawPolyline(snappedPoints)
            } else {
                showError()
            }
        }
    }
}

Current problem:

The function getSnappedPoints() is called when I click on specific item. For the very first time it actually calls google API and result is > 0 (size), but if I go back and click second time on the same/other item, getSnappedPoints() is called, paths is not Empty, but somehow it does not call googleRoadsRepository.getSnappedPoints(it) and kinda skips that step in debug and all I can see is that result is always 0. What could cause this?

3
  • 1
    What is Repository.getPoints()? It has a parameter while the getPoints shown in the question has no argument. What is result2 in this context here? which type of if condition is this? Commented Feb 17, 2020 at 14:03
  • Please formulate the question so it has only the explicitly needed information - as code as description - since I don't understand your problem. What should work? How would a test look like? Why does it not work? Commented Feb 17, 2020 at 17:31
  • Please do not vandalize your posts. Once you've posted a question, it belongs to the Stack Overflow community at large (under the CC BY-SA license). Please see How does deleting work? What can cause a post to be deleted, and what does that actually mean? What are the criteria for deletion? for more information on how deleting content works on Stack Overflow. Commented May 27, 2020 at 0:37

2 Answers 2

2

Each coroutine (launch) is a unit of concurrency, if you want to run each request concurrently, you'll have to do a launch/async for each concurrent request.

private fun getPoints() {
    val multipleParams = Utils.getArrayListOfParams()

    coroutineJob = CoroutineScope(Dispatchers.IO).launch {
        val results = multipleParams.map {
            async { Repository.getPoints(it) }
        }.awaitAll()

        // Do something with results, once all have been gotten.
        // result and result2, result[it] has finished, continue)
    }
}

Bonus:

  • If you create a CoroutineScope without cancelling it, then you should use GlobalScope as an optimisation. (Like so GlobalScope.launch(Dispatchers.IO))
  • You don't need Dispatchers.IO to do this, since it appears you are not blocking the coroutine/thread. Dispatchers.Default will do just fine.
  • I'm not sure what you're using coroutineJob variable for but it looks suspicious.
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for information. I actually want to block and wait all the responses so I do have to use Dispatchers.IO if Im not mistaken. Also, I edited post so maybe you can take a look. I get results for the first time when I call function, but after that awaitAll step feels like is skipping (when I use debugger) and it goes straight to result, which obviously is 0.
When you say block, are you sure you don't mean suspend?
Debugging and coroutines aren't very good friends just yet (since the call stack is transformed), so I wouldn't fully rely on it. Trying just using println statements to confirm the behaviour.
I meant suspend, yes. Noted about debugger.
0

While you at IO thread so what is the necessity of using async + await? At the IO scope, you are free to suspend and wait for the response.

Try this one and notify me to know what will the result?

private fun getSnappedPoints() {
val paths = Utils.getPathFromLocations(locations)

CoroutineScope(Dispatchers.IO).launch {
    val results = paths.map {
        googleRoadsRepository.getSnappedPoints(it)
    }

    // rest of you code...
}

4 Comments

Zero results. What I want is to block thread and not move further till the results variable has been filled/received from API call and only then do changes on UI thread (meanwhile show loader).
@PeteWonder Which part of code not executed? is googleRoadsService.getSnappedPoints(path).await() part executed? Could you see the log of API call in the Logcat? or the API call is not triggered?
Looks like that. I set debug point to googleRoadsRepository.getSnappedPoints(it), but it automatically hops to next line of Timber log.
@PeteWonder It's weird, Are you sure the paths is not empty?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.