async/await for Android built upon coroutines introduced in Kotlin 1.1
A Kotlin library for Android to write asynchronous code in a simpler and more reliable way using
async/
awaitapproach, like:
async { progressBar.visibility = View.VISIBLE // Release main thread and wait until text is loaded in background thread val loadedText = await { loadFromServer() } // Loaded successfully, come back in UI thread and show the result txtResult.text = loadedText progressBar.visibility = View.INVISIBLE }
As you see in the example above, you can write asynchronous code in a imperative style, step by step. Calling
awaitto run code in background doesn't lock the UI thread. And execution continues in UI thread after background work is finished. There is no magic, see how it works.
compile 'co.metalab.asyncawait:asyncawait:1.0.0'
async
Coroutine code has to be passed as a lambda in
asyncfunction
Kotlin async { // Coroutine body }
await
Long running code has to be passed as a lambda in
awaitfunction
Kotlin async { val result = await { //Long running code } // Use result }You may have many
awaitcalls inside
asyncblock, or have
awaitin a loop
async { val repos = await { github.getRepos() } showList(repos) repos.forEach { repo -> val stats = await { github.getStats(repo.name) } showStats(repo, stats) } }
awaitWithProgress
Use it to show loading progress, its second parameter is a progress handler.
Kotlin val loadedText = awaitWithProgress(::loadTextWithProgress) { // Called in UI thread progressBar.progress = it progressBar.max = 100 }A data loading function (like the
loadTextWithProgressabove) should have a functional parameter of type
(P) -> Unitwhich can be called in order to push progress value. For example, it could be like:
Kotlin private fun loadTextWithProgress(handleProgress: (Int) -> Unit): String { for (i in 1..10) { handleProgress(i * 100 / 10) // in % Thread.sleep(300) } return "Loaded Text" }
try/catch
async { try { val loadedText = await { // throw exception in background thread } // Process loaded text } catch (e: Exception) { // Handle exception in UI thread } }
onErrorblock
Could be more convenient, as resulting code has fewer indents.
onErrorcalled only if exception hasn't been handled in
try/catch.
Kotlin async { val loadedText = await { // throw exception in background thread } // Process loaded text }.onError { // Handle exception in UI thread }
Unhandled exceptions and exception delivered in
onErrorwrapped by
AsyncExceptionwith convenient stack trace to the place where
awaitbeen called originally in UI thread
finallyexecution
finallyalways executed after calling
onErroror when the coroutine finished successfully.
Kotlin async { // Show progress await { } }.onError { // Handle exception }.finally { // Hide progress }
The library has
Activity.asyncand
Fragment.asyncextension functions to produce more safe code. So when using
asyncinside Activity/Fragment, coroutine won't be resumed if
Activityis in finishing state or
Fragmentis detached.
Long running background code referencing any view/context may produce memory leaks. To avoid such memory leaks, call
async.cancelAll()when all running coroutines referencing current object should be interrupted, like
Kotlin override fun onDestroy() { super.onDestroy() async.cancelAll() }The
asyncis an extension property for
Anytype. So calling
[this.]async.cancelAllintrerrupts only coroutines started by
[this.]async {}function.
The library has a convenient API to work with Retrofit and rxJava.
awaitSuccessful(retrofit2.Call)
Returns
Response.body()if successful, or throws
RetrofitHttpErrorwith error response otherwise.
Kotlin async { reposList = awaitSuccessful(github.listRepos(userName)) }
Waits until
observableemits first value.
Kotlin async { val observable = Observable.just("O") result = await(observable) }
You can create your own
awaitimplementations. Here is example of rxJava extension to give you idea. Just return the result of calling
AsyncController.awaitwith your own lambda implementation. The code inside
awaitblock will be run on a background thread.
Kotlin suspend fun AsyncController.await(observable: Observable): V = this.await { observable.toBlocking().first() }
The library is built upon coroutines introduced in Kotlin 1.1.
The Kotlin compiler responsibility is to convert coroutine (everything inside
asyncblock) into a state machine, where every
awaitcall is a non-blocking suspension point. The library is responsible for thread handling, error handling and managing state machine. When background computation is done the library delivers result back into UI thread and resumes coroutine execution.