I'm using Android Ktor for fetching information an...
# ktor
h
I'm using Android Ktor for fetching information and also downloading file. When downloading files in
<http://Dispatchers.IO|Dispatchers.IO>
at different coroutine, I tried to fetch information with different endpoint, showing that It wait until the download progress is completed before handling it. Is that normal? How can I make it work async? Here is my manifest:
Copy code
override fun getUpdateManifest(url: String, callback: (Either<UpdateModel>) -> Unit) {
        suspend fun internalGetManifest() {
            try {
                val response = updateApi.getManifest(url).toDomain() // << it waits here until download is completed.
                callback(Either.Success(response))
            } catch (e: Exception) {
                callback(Either.Error(message = e.localizedMessage ?: "Unknown error", cause = e))
            }
        }
        CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch {
            internalGetManifest()
        }
    }
Here is my download using WorkManager:
Copy code
override suspend fun doWork(): Result = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
        val url = inputData.getString(PARAM_URL) ?: return@withContext Result.failure()
        val destinationFile = UpdateManager.destinationFile

        var downloadResult: Result = Result.failure()

        try {
            updateRepository.downloadUpdate(url = url, destination = destinationFile)
                .onStart { setProgress(workDataOf(PROGRESS_ARG to 0f)) }
                .onCompletion { downloadResult = Result.success() }
                .collect { setProgress(workDataOf(PROGRESS_ARG to it)) }
        } catch (e: Exception) {
            downloadResult = Result.failure()
            e.printStackTrace()
        }

        return@withContext downloadResult
    }
And my Ktor client:
Copy code
val ktorHttpClient = HttpClient(Android) {
    install(JsonFeature) {
        serializer = KotlinxSerializer(kotlinx.serialization.json.Json {
            prettyPrint = true
            isLenient = true
            ignoreUnknownKeys = true
        })

        engine {
            connectTimeout = TIME_OUT
            socketTimeout = TIME_OUT
        }
    }

    install(Logging) {
        logger = object : Logger {
            override fun log(message: String) {
                Log.v("Logger Ktor =>", message)
            }
        }
        level = LogLevel.ALL
    }

    install(ResponseObserver) {
        onResponse {
            Log.d("HTTP status:", "${it.status.value}")
        }
    }

    install(DefaultRequest) {
        headers.append(ContentType, io.ktor.http.ContentType.Application.Json.toString())
    }
}
a
Have you tried to use async?
h
@Aleksei Tirman [JB] Hi can you give me an example? On the other hand, when I replace getting manifest by Retrofit, it works as expected.
Copy code
interface UpdateRetrofitService {
    @GET
    suspend fun getManifest(@Url url: String): UpdateDto
}
Copy code
override fun getUpdateManifest(url: String, callback: (Either<UpdateModel>) -> Unit) {
        suspend fun internalGetManifest() {
            try {
                val response = retrofitService.getManifest(url).toDomain()
                callback(Either.Success(response))
            } catch (e: Exception) {
                callback(Either.Error(message = e.localizedMessage ?: "Unknown error", cause = e))
            }
        }
        CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch {
            internalGetManifest()
        }
    }
a
Here is an example of the concurrent requests:
Copy code
fun main(): Unit = runBlocking {
    val client = HttpClient(CIO)
    val deferred1 = async {
        client.get("<https://httpbin.org/get>").bodyAsText()
    }

    val deferred2 = async {
        client.get("<https://httpbin.org/get>").bodyAsText()
    }

    val responses = listOf(deferred1, deferred2).awaitAll()
    println(responses)
}
👍 1