简体   繁体   中英

Calling multiple API requests and waiting for response using Kotlin Coroutines

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?

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.

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...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM