Kotlin Coroutines 101 - Android Conference Talks

Share

Summary

This video provides an introduction to Kotlin Coroutines for Android development. It covers the problems coroutines solve, how to use them for asynchronous programming, structured concurrency for managing coroutine lifecycles, and strategies for testing coroutines.

Highlights

Introduction to Coroutines: Solving Asynchronous Programming Problems
00:00:06

Coroutines simplify asynchronous programming in Android. Synchronous code, like a network request on the main UI thread, blocks the UI, leading to an unresponsive application. Callbacks offer a solution by moving the network request to a different thread, freeing up the UI thread. However, callbacks can lead to 'callback hell' with nested logic and difficult error propagation.

Coroutines: Synchronous Code with Asynchronous Power
00:03:17

Coroutines offer the simplicity of synchronous code while retaining the power of asynchronous operations. By adding the 'suspend' modifier to a function, the Kotlin compiler handles the asynchronous logic, suspending execution without blocking the thread and resuming when the operation is complete. The compiler generates callbacks called 'continuations' under the hood.

Changing Threads with Dispatchers and `withContext`
00:05:45

To move execution to a different thread, coroutines use `withContext` and Dispatchers. `withContext` is a suspend function that takes a Dispatcher as a parameter, allowing specific blocks of code to run on `Dispatchers.IO` (for network/disk), `Dispatchers.Default` (for CPU-intensive tasks), or `Dispatchers.Main` (for UI or fast non-blocking code). This ensures operations don't block the main UI thread.

What is a Coroutine and Structured Concurrency
00:07:16

A coroutine can be thought of as a 'runnable with superpowers,' allowing asynchronous code to be expressed sequentially. Structured concurrency is a design pattern that prevents memory leaks and ensures proper management of coroutines by introducing 'CoroutineScope'. A scope tracks created coroutines, allows their cancellation, and handles exceptions. Cancelling a scope cancels all child coroutines and prevents new ones from being started within it.

Handling Exceptions with Coroutine Jobs
00:11:10

Coroutine scopes can take a 'Job' to define their lifecycle and how exceptions are handled. A regular `Job` will cancel sibling coroutines and propagate exceptions upwards, potentially crashing the application. `SupervisorJob` provides an alternative for UI-related scopes, where a child's failure does not affect other children, and the scope itself is not automatically cancelled. Exceptions in `SupervisorJob` still need to be handled, often with `try-catch` blocks.

Creating Coroutines: `launch` vs. `async`
00:13:03

`launch` and `async` are two ways to create new coroutines from within a scope. `launch` is for 'fire-and-forget' operations that don't return a result, like logging. `async` creates a coroutine that returns a result, wrapped in a `Deferred` object. The `await()` function on a `Deferred` object suspends execution until the async computation finishes and returns its value. Both `launch` and `async` are not suspend functions and can take a dispatcher.

Exception Handling with `launch` and `async`
00:15:25

Exception handling differs between `launch` and `async`. `launch` throws exceptions immediately, which can be caught using a `try-catch` block around the launched coroutine. `async` holds onto an exception until `await()` is called, requiring the `try-catch` block to wrap the `await()` call.

Cooperative Cancellation and Suspend Function Declaration
00:16:19

Coroutine cancellation is cooperative. Heavy computations within a coroutine may not automatically cancel unless they explicitly check for cancellation using functions like `ensureActive` or `yield`. A function should be marked `suspend` only if it calls other suspend functions, otherwise, it can start a coroutine using `launch` or `async` without being a suspend function itself. The rule of thumb is not to mark a function suspend unless forced to.

Testing Coroutines
00:18:09

Testing asynchronous code for deterministic behavior can be challenging. For tests that don't trigger new coroutines, `runBlocking` can be used to execute the suspend function synchronously within the test. For tests that trigger new coroutines, directly using `runBlocking` can lead to unreliable results due to asynchronous execution. A better practice is to inject dispatchers and use `TestCoroutineDispatcher` with `runBlockingTest` in tests. This allows for sequential execution of coroutines on the test dispatcher, ensuring all coroutines complete before assertions. `TestCoroutineDispatcher` also offers features like `pauseDispatcher` and `resumeDispatcher` for granular control over coroutine execution in tests.

Summary and Further Resources
00:23:10

The video summarizes the key concepts covered: simplifying asynchronous programming, dispatcher usage with `withContext`, understanding coroutines as 'super-runnables', internal workings (continuations), structured concurrency principles, `launch` vs. `async`, exception handling, suspend function declaration rules, and testing strategies using `TestCoroutineDispatcher`. For further learning, the speaker recommends available Codelabs (basic and advanced) and talks from Android Developer Summit 2019 and KotlinConf 2019.

Recently Summarized Articles

Loading...