## Asynchronous work {#async} ### With return values {#async-return} #### Kotlin Traditionally, asynchronous work on Android that results in an output value would use a callback; however, better alternatives exist for libraries. Kotlin libraries should consider [coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) and `suspend` functions for APIs according to the following rules, but please refer to the guidance on [allowable dependencies](#dependencies-coroutines) before adding a new dependency on coroutines. Kotlin suspend fun vs blocking | Behavior ------------------------------------ | -------------------------- blocking function with @WorkerThread | API is blocking suspend | API is async (e.g. Future) In general, do not introduce a suspend function entirely to switch threads for blocking calls. To do so correctly requires that we allow the developer to configure the Dispatcher. As there is already a coroutines-based API for changing dispatchers (withContext) that the caller may use to switch threads, it is unecessary API overhead to provide a duplicate mechanism. In addition, it unecessary limits callers to coroutine contexts. ```kotlin // DO expose blocking calls as blocking calls @WorkerThread fun blockingCall() // DON'T wrap in suspend functions (only to switch threads) suspend fun blockingCallWrappedInSuspend( dispatcher: CoroutineDispatcher = Dispatchers.Default ) = withContext(dispatcher) { /* ... */ } // DO expose async calls as suspend funs suspend fun asyncCall(): ReturnValue // DON'T expose async calls as a callback-based API (for the main API) fun asyncCall(executor: Executor, callback: (ReturnValue) -> Unit) ``` #### Java Java libraries should prefer `ListenableFuture` and the [`CallbackToFutureAdapter`](https://developer.android.com/reference/androidx/concurrent/futures/CallbackToFutureAdapter) implementation provided by the `androidx.concurrent:concurrent-futures` library. Functions and methods that return `ListenableFuture` should be suffixed by, `Async` to reserve the shorter, unmodified name for a `suspend` method or extension function in Kotlin that returns the value normally in accordance with structured concurrency. Libraries **must not** use `java.util.concurrent.CompletableFuture`, as it has a large API surface that permits arbitrary mutation of the future's value and has error-prone defaults. See the [Dependencies](#dependencies) section for more information on using Kotlin coroutines and Guava in your library. ### Cancellation Libraries that expose APIs for performing asynchronous work should support cancellation. There are *very few* cases where it is not feasible to support cancellation. Libraries that use `ListenableFuture` must be careful to follow the exact specification of [`Future.cancel(boolean mayInterruptIfRunning)`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html?is-external=true#cancel-boolean-) behavior. ```java {.bad} @Override public boolean cancel(boolean mayInterruptIfRunning) { // Does not support cancellation. return false; } ``` ```java {.bad} @Override public boolean cancel(boolean mayInterruptIfRunning) { // Aggressively does not support cancellation. throw new UnsupportedOperationException(); } ``` ```java {.good} @Override public boolean cancel(boolean mayInterruptIfRunning) { // Pseudocode that ignores threading but follows the spec. if (mCompleted || mCancelled || mRunning && !mayInterruptIfRunning) { return false; } mCancelled = true; return true; } ``` ### Avoid `synchronized` methods Whenever multiple threads are interacting with shared (mutable) references those reads and writes must be synchronized in some way. However synchronized blocks make your code thread-safe at the expense of concurrent execution. Any time execution enters a synchronized block or method any other thread trying to enter a synchronized block on the same object has to wait; even if in practice the operations are unrelated (e.g. they interact with different fields). This can dramatically reduce the benefit of trying to write multi-threaded code in the first place. Locking with synchronized is a heavyweight form of ensuring ordering between threads, and there are a number of common APIs and patterns that you can use that are more lightweight, depending on your use case: * Compute a value once and make it available to all threads * Update Set and Map data structures across threads * Allow a group of threads to process a stream of data concurrently * Provide instances of a non-thread-safe type to multiple threads * Update a value from multiple threads atomically * Maintain granular control of your concurrency invariants