async/await for Android built upon coroutines introduced in Kotlin 1.1

Overview

Async/Await

A Kotlin library for Android to write asynchronous code in a simpler and more reliable way using async/await approach, 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 await to 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.

Dependency

compile 'co.metalab.asyncawait:asyncawait:1.0.0'

Usage

async

Coroutine code has to be passed as a lambda in async function

async {
   // Coroutine body
}

await

Long running code has to be passed as a lambda in await function

async {
   val result = await {
      //Long running code
   }
   // Use result
}

You may have many await calls inside async block, or have await in 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.

val loadedText = awaitWithProgress(::loadTextWithProgress) {
         // Called in UI thread
         progressBar.progress = it
         progressBar.max = 100
      }

A data loading function (like the loadTextWithProgress above) should have a functional parameter of type (P) -> Unit which can be called in order to push progress value. For example, it could be like:

private fun loadTextWithProgress(handleProgress: (Int) -> Unit): String {
   for (i in 1..10) {
      handleProgress(i * 100 / 10) // in %
      Thread.sleep(300)
   }
   return "Loaded Text"
}

Handle exceptions using try/catch

async {
   try {
      val loadedText = await {
         // throw exception in background thread
      }
      // Process loaded text
   } catch (e: Exception) {
      // Handle exception in UI thread
   }
}

Handle exceptions in onError block

Could be more convenient, as resulting code has fewer indents. onError called only if exception hasn't been handled in try/catch.

async {
   val loadedText = await {
      // throw exception in background thread
   }
   // Process loaded text
}.onError {
   // Handle exception in UI thread
}

Unhandled exceptions and exception delivered in onError wrapped by AsyncException with convenient stack trace to the place where await been called originally in UI thread

finally execution

finally always executed after calling onError or when the coroutine finished successfully.

async {
   // Show progress
   await { }
}.onError {
   // Handle exception
}.finally {
   // Hide progress
}

Safe execution

The library has Activity.async and Fragment.async extension functions to produce more safe code. So when using async inside Activity/Fragment, coroutine won't be resumed if Activity is in finishing state or Fragment is detached.

Avoid memory leaks

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

override fun onDestroy() {
      super.onDestroy()
      async.cancelAll()
}

The async is an extension property for Any type. So calling [this.]async.cancelAll intrerrupts only coroutines started by [this.]async {} function.

Common extensions

The library has a convenient API to work with Retrofit and rxJava.

Retorift

  • awaitSuccessful(retrofit2.Call)

Returns Response<V>.body() if successful, or throws RetrofitHttpError with error response otherwise.

async {
   reposList = awaitSuccessful(github.listRepos(userName))
}

rxJava

  • await(Observable)

Waits until observable emits first value.

async {
   val observable = Observable.just("O")
   result = await(observable)
}

How to create custom extensions

You can create your own await implementations. Here is example of rxJava extension to give you idea. Just return the result of calling AsyncController.await with your own lambda implementation. The code inside await block will be run on a background thread.

suspend fun <V> AsyncController.await(observable: Observable<V>): V = this.await {
   observable.toBlocking().first()
}

How it works

The library is built upon coroutines introduced in Kotlin 1.1.

The Kotlin compiler responsibility is to convert coroutine (everything inside async block) into a state machine, where every await call 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.

Comments
  • Add idling resource direct support

    Add idling resource direct support

    This is my proposed solution for issue https://github.com/metalabdesign/AsyncAwait/issues/25.

    • Create two callback methods that are used whenever a coroutine starts execution in a different thread or when all coroutines finish.
    • Create an idling resource implementation that register those callbacks and resumes execution only if there are no coroutines being executed.
    • Create an espresso test that verifies the new feature works just as expected.

    I followed a very similar approach to what Jake Wharton used for OkHttp3 (https://github.com/JakeWharton/okhttp-idling-resource/blob/master/src/main/java/com/jakewharton/espresso/OkHttp3IdlingResource.java). I thought it was better to create two different callbacks to hide as much as possible from the implementation, though.

    Personally, I'd recommend to release this feature as a different module so that developers can load the new dependency only for testing builds but being such a small feature I'd understand including it in the very same library.

    opened by Serchinastico 3
  • Question: Code execution sequence

    Question: Code execution sequence

    Thanks for this library - it is really nice to see Async/Await coming to Android. I have a question about the sequence of execution. Consider this example

    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
    }
    someOtherCodeHere()
    yetMoreAwesomeCodeHere()
    

    I understand that when we hit await { loadFromServer() }, the execution of the async block is suspended and the control proceeds to someOtherCodeHere(). Is this right?

    Also, what happens later when loadFromServer() returns? I assume then txtResult.text = loadedText and the rest of the async block is executed. But, then what? Where does the execution proceed after an async block that had been suspended is un-suspended?

    opened by curioustechizen 2
  • Avoid memory leaks/cancel tasks using async.cancelAll

    Avoid memory leaks/cancel tasks using async.cancelAll

    @tsuharesu please take a look how we gonna solve the memory leak issues by calling async.cancelAll. This function should be call when object is not needed anymore (like in Activtity.onDestroy). The significant changes are in AsyncUi.kt file. Thanks!

    opened by pilgr 2
  • How does this compare to kotlinx-coroutines-android?

    How does this compare to kotlinx-coroutines-android?

    Hi, I discovered JetBrains (and probably other contributors) made a coroutines Android implementation on the kotlinx.couroutines repo, and I assume you were aware of this while building this library.

    Could you add a word in the README.md or another file in the repo on how your implementation and the kotlinx one compare together in different regards such as performance, API design and features (and more if useful)?

    opened by LouisCAD 1
  • Library manifest shouldn't define supportsRtl and allowBackup

    Library manifest shouldn't define supportsRtl and allowBackup

    Hi,

    In a project where I intentionally set android:allowBackup="false" in the <application .../> tag of my manifest, and it clashes with this library, because it's defined here. These attributes should be removed.

    opened by LouisCAD 0
  • Support for idling resource

    Support for idling resource

    Hi,

    I was just wondering how hard is to implement/expose some sort of mechanism to be able to create idling resources that wait for the coroutines to finish.

    I can try sending you a PR if you point me in the right direction on what you think is the best way to do it. I think it'd be a great addition to the library.

    opened by Serchinastico 3
  • Callback for cancelling

    Callback for cancelling

    Given a situation where UI elements depend on the AsyncController (like progress bars) while having to cancel tasks, it's necessary to know when a task was cancelled in order to hide progress and such

    opened by jlnstrk 1
  • Use Lifecycle architecture component to clear up async

    Use Lifecycle architecture component to clear up async

    Currently we need to cancel all the async on destroying Activity.

    override fun onDestroy() { super.onDestroy() async.cancelAll() }

    With the new component called Lifecycle (Reference link below) we can skip it and the library can itself take care of it by understanding the lifecycle.

    Refer: Lifecycle Architecture Component

    opened by AkshayChordiya 0
  • Parallel execution?

    Parallel execution?

    When I try to execute several tasks in parallel my code looks ugly.

    async {
               await {
                   async {
                       await {
                           async {
                               await {
                                   val it = System.currentTimeMillis()
                                   while (System.currentTimeMillis() - it < 5000);
                                   Log.w("Executing coroutine", "Task1")
                               }
                           }
                           val it = System.currentTimeMillis()
                           while (System.currentTimeMillis() - it < 5000);
                           Log.w("Executing coroutine", "Task2")
                       }
                   }
                   val it = System.currentTimeMillis()
                   while(System.currentTimeMillis() - it < 5000 );
                   Log.w("Executing coroutine", "Task3")
               }
    
               Log.w("Executing coroutine", "All tasks completed")
           }
    

    Is any way to make parallel execution looks more elegant? Something like this

     async {
                awaitParallel  {
                    val it = System.currentTimeMillis()
                    while (System.currentTimeMillis() - it < 5000);
                    Log.w("Executing coroutine", "Task1")
                }
                awaitParallel  {
                    val it = System.currentTimeMillis()
                    while (System.currentTimeMillis() - it < 5000);
                    Log.w("Executing coroutine", "Task2")
                }
                awaitParallel  {
                    val it = System.currentTimeMillis()
                    while (System.currentTimeMillis() - it < 5000);
                    Log.w("Executing coroutine", "Task3")
                }
                Log.w("Executing coroutine", "All tasks completed")
            }
    
    opened by atonamy 1
  • Add support for Support Fragments

    Add support for Support Fragments

    Fragments from the support-v4 library are recommended for usage over native Fragments, given they are always up-to-date on bug fixes and when new features are added. Plus, it is more likely that someone will be using Support Fragments in their app rather than native Fragments.

    opened by nasahapps 0
Releases(1.0.0)
  • 1.0.0(Mar 1, 2017)

  • 0.9.5(Feb 17, 2017)

  • 0.9(Jan 26, 2017)

  • 0.8(Jan 16, 2017)

  • 0.7(Oct 20, 2016)

  • 0.6(Sep 21, 2016)

    • Added async.cancelAll() function to cancel all tasks started with await for current object. Good to call in Activity.onDestroy to avoid memory leaks;
    • Added finally{} block to run code after onError or when coroutine finished succesfully;
    • Every object using async now has its own single thread executor to run await tasks. E.g. one for each Activity or Fragment instance;
    • try/catch gained more priority in exception handling inside async{} block;
    • Unhandled exceptions now wrapped in AsyncException to provide better stacktrace to the place where async been called originally in UI thread.
    Source code(tar.gz)
    Source code(zip)
Owner
MetaLab
We make internets.
MetaLab
AsyncSport - AsyncSports Async sports is very simple application that shows athletes video feeds

AsyncLabs Interview Solution ?? Writing AsyncLabs Interview Solution App using A

David Innocent 0 Jan 7, 2022
Recycler-coroutines - RecyclerView Auto Add Data Using Coroutines

Sample RecyclerView Auto Add With Coroutine Colaborator Very open to anyone, I'l

Faisal Amir 8 Dec 1, 2022
Built with Jetpack compose, multi modules MVVM clean architecture, coroutines + flow, dependency injection, jetpack navigation and other jetpack components

RickAndMortyCompose - Work in progress A simple app using Jetpack compose, clean architecture, multi modules, coroutines + flows, dependency injection

Daniel Waiguru 9 Jul 13, 2022
A minimal notes application in Jetpack Compose with MVVM architecture. Built with components like DataStore, Coroutines, ViewModel, LiveData, Room, Navigation-Compose, Coil, koin etc.

Paper - A Minimal Notes App A minimal notes application in Jetpack Compose with MVVM architecture. Built with components like DataStore, Coroutines, V

Akshay Sharma 139 Jan 2, 2023
Opinionated Redux-like implementation backed by Kotlin Coroutines and Kotlin Multiplatform Mobile

CoRed CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creato

Kittinun Vantasin 28 Dec 10, 2022
Android To-Do MVVM Architecture App written in Kotlin.(ViewModel, ROOM, Livedata, Coroutines)

MVVM-To-Do-App A To-Do application written in kotlin using Android Architectural components What's new? Room + Coroutines - Upgraded Room to v2.1. Roo

Naveen T P 77 Dec 8, 2022
A Kotlin Android library for content provider queries with reactive streams and coroutines.

Pickpocket An Android library for content provider queries with reactive streams and coroutines. Calendar Contacts SMS MMS Files/Media Call Log Bookma

Chris Basinger 27 Nov 14, 2022
Android Library for requesting Permissions with Kotlin Coroutines or AndroidX LiveData

PEKO PErmissions with KOtlin Android Permissions with Kotlin Coroutines or LiveData No more callbacks, builders, listeners or verbose code for request

Marko Devcic 133 Dec 14, 2022
Image loading for Android backed by Kotlin Coroutines.

An image loading library for Android backed by Kotlin Coroutines. Coil is: Fast: Coil performs a number of optimizations including memory and disk cac

Coil 8.8k Jan 7, 2023
Android News Reader app. Kotlin Coroutines, Retrofit and Realm

News Reader Android News Reader app Code that follows Packt Publishing Kotlin in Practice Video Course Example of Kotlin Coroutine usage, with Realm a

Marko Devcic 22 Oct 3, 2022
Extension functions over Android's callback-based APIs which allows writing them in a sequential way within coroutines or observe multiple callbacks through kotlin flow.

callback-ktx A lightweight Android library that wraps Android's callback-based APIs into suspending extension functions which allow writing them in a

Sagar Viradiya 171 Oct 31, 2022
A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

A simple, classic Kotlin MVI implementation based on coroutines with Android support, clean DSL and easy to understand logic

Nek.12 4 Oct 31, 2022
🍭 GithubSearchKMM - Github Repos Search - Android - iOS - Kotlin Multiplatform Mobile using Jetpack Compose, SwiftUI, FlowRedux, Coroutines Flow, Dagger Hilt, Koin Dependency Injection, shared KMP ViewModel, Clean Architecture

GithubSearchKMM Github Repos Search - Kotlin Multiplatform Mobile using Jetpack Compose, SwiftUI, FlowRedux, Coroutines Flow, Dagger Hilt, Koin Depend

Petrus Nguyễn Thái Học 50 Jan 7, 2023
Library to use Kotlin Coroutines from Swift code in KMP apps

KMP-NativeCoroutines A library to use Kotlin Coroutines from Swift code in KMP apps. Flows Kotlin Create an extension property to expose the Flow as a

Rick Clephas 508 Jan 3, 2023
A framework for writing composable parsers based on Kotlin Coroutines.

Parsus A framework for writing composable parsers based on Kotlin Coroutines. val booleanGrammar = object : Grammar<BooleanExpression>() { val ws

Aleksei Semin 28 Nov 1, 2022
Server Sent Events (SSE) client multiplatform library made with Kotlin and backed by coroutines

OkSSE OkSSE is an client for Server Sent events protocol written in Kotlin Multiplatform. The implementation is written according to W3C Recommendatio

BioWink GmbH 39 Nov 4, 2022
RSocket, WebFlux, Reactor, Kotlin, Coroutines

RSocket messaging services communication RSocket, WebFlux, Reactor, Kotlin, Coroutines edge-service <-~-> web-service <-~-> coroutines-service <-~-> r

Maksim Kostromin 2 Nov 12, 2021
A Kotlin coroutines wrapper for IndexedDB.

Kotlin IndexedDB A wrapper around IndexedDB which allows for access from Kotlin/JS code using suspend blocks and linear, non-callback based control fl

JUUL Labs 10 Dec 11, 2022
Small lib for recovering stack trace in exceptions thrown in Kotlin coroutines

Stacktrace-decoroutinator Library for recovering stack trace in exceptions thrown in Kotlin coroutines. Supports JVM(not Android) versions 1.8 or high

null 104 Dec 24, 2022