Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin: How to perform an async function and wait for it to finish?

Tags:

android

kotlin

I'm writting my first app in Kotlin, so I'm pretty new to this. One of the functions it performs is to read something from an API, and then update the screen based on the result.

I have tried lots of things with the coroutines, but nothing seems to work.

For example, I have something like this:

private fun readAPI() {
    runBlocking {
        fun rAPI() = async {
            val api = "..."
            result = URL(api).readText()
        }
        println(tag, "Result: " + rAPI().await())
    }
}

And lots of different approaches. Nothing seems to work. In the above case I'm getting an exception "android.os.NetworkOnMainThreadException".

The only thig that has worked so far, is something using OkHttp3 as described here: https://rstopup.com/como-hacer-una-solicitud-a-la-api-de-kotlin.html (it's in Spanish, but you'll get the idea), and this works, it brings the API response, I parse it, fill in my sqlite3 database and so on. But, since I don't know when the API ends, I can't update the screen controls. And if I try to do it serially, I get an exception that only the thread which started the activity is the one that can update the activity or something like that.

I've seen, and follow LOTS of tutorials that talks about suspend functions, launch, etc., and they try to mimick an API call with delay(), those tutorials work perfectly, until I try to do a real API call.

So, can you point me to a full example on how to call an API with Kotlin, and then update some screen elements?

EDIT I'm editing changing the fun by val:

runBlocking {
  val rAPI = async {
    val api = "..."
    URL(api).readText()
  }
  Log.w(tag, rAPI.await())
}

I got the "android.os.NetworkOnMainThreadException" exception.

like image 940
luisfer Avatar asked Jun 25 '19 23:06

luisfer


People also ask

How do you wait for async to finish in Kotlin?

async-await Kotlin's async function allows running concurrent coroutines and returns a Deferred<T> result. Deferred is a non-blocking cancellable future to act as a proxy for a result that is initially unknown. For example, by calling Deferred#wait method, we wait until the task is done, and then we have the result.

How do you wait for a thread to finish Kotlin?

To wait for a coroutine to finish, you can call Job. join . join is a suspending function, meaning that the coroutine calling it will be suspended until it is told to resume. At the point of suspension, the executing thread is released to any other available coroutines (that are sharing that thread or thread pool).

How do you use async and await in Kotlin?

In order to migrate to the async/await pattern, you have to return the async() result from your code, and call await() on the Deferred , from within another coroutine. By doing so, you can remove callbacks you used to use, to consume asynchronously provided values.

How do you wait for a coroutine to finish Android?

We can wait for the coroutine to finish by calling join() on the Job. For example, suppose we have a suspend function to download some files. We can launch this coroutine and capture the resulting job, which we can later use to join — to wait for the operation to complete.


1 Answers

Since you want to use coroutine-async ways, you must tell main thread to waiting for you. you need to use suspend function or block to do this.

GlobalScope.launch {
    suspend {
        Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
        delay(10000)
        withContext(Dispatchers.Main) {
            Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
        }
    }.invoke()
}

result log

09:36:09.500 D/: #runs on DefaultDispatcher-worker-1
// 10 seconds later
09:36:19.678 D/: #runs on main 

I think this should do the trick.

However I suggest you to understand how to use OkHttp/Volley by passing callbacks(with onSuccess and onFail or something) into that. Or Retrofit2 with RxJavato handle many of these issues.

EDIT For the Module with the Main dispatcher had failed to initialize error, replace the withContext() line with this

withContext(Handler(Looper.getMainLooper()).asCoroutineDispatcher())

EDIT Now don't use RxJava, use liveData/LiveEvent to implement the observer pattern

like image 177
Wesely Avatar answered Oct 31 '22 08:10

Wesely