Coroutines in Android
Multi-threading in Android allows tasks to be handled on separate threads without blocking the main UI thread. Common solutions include AsyncTask (now deprecated), ThreadPoolExecutor, and RxJava. Third-party libraries such as Volley and Retrofit are also popular. Coroutines offer a purely Kotlin-based, lightweight solution with minimal risk of memory leaks and several other advantages. They enable concurrent execution of code without blocking the main thread, eliminating the need for callbacks to switch between threads. Coroutines are often referred to as lightweight threads. However, strictly speaking, they are not threads; they are blocks of code executed by functions on predefined threads. Coroutines can be paused, stopped, and resumed on the threads they run on, avoiding the inefficiency of keeping threads idle. This makes them an effective tool for writing asynchronous, non-blocking code that is faster and requires fewer resources compared to traditional threads.
Main Coroutine Features
- Coroutines are not tied to specific threads and can suspend execution on one thread and resume on another.
- Multiple coroutines can run on a single thread, each with its own lifecycle. Thanks to suspension functions, coroutines do not block the thread they run on, allowing them to start, pause, and resume within a coroutine context.
-
All coroutines must run inside a Coroutine Scope, which defines the coroutine context and manages their lifecycle. Cancelling a scope destroys all coroutines within it. Common coroutine scopes include:
- GlobalScope: Coroutines live as long as the application.
- LifecycleScope: Coroutines live as long as the activity or fragment lifecycle.
- ViewModelScope: Coroutines live as long as the ViewModel lifecycle.
- runBlocking: Creates a temporary scope and blocks the current thread until its child coroutines complete.
- CoroutineScope: Tied to custom use cases, independent of standard Android components.
-
Coroutines use dispatchers to specify the threads they run on. Dispatchers include:
- Dispatchers.Default: For CPU-intensive tasks.
- Dispatchers.IO: For I/O-bound tasks such as file operations, network requests, or database interactions.
- Dispatchers.Main: For tasks on the main (UI) thread.
- Dispatchers.Unconfined: Without a specific thread or thread pool.
launch
orasync
. -
Coroutine builders include:
- runBlocking: Blocks the underlying thread.
- launch: Launches a coroutine concurrently without returning a result.
- async: Launches a coroutine asynchronously and returns a result.
Kotlin's Suspend Function
Suspend functions are functions that can be paused and resumed. They are declared using the suspend
keyword before the function name and can only be called from a coroutine or another suspend function. Suspend functions enable coroutines to be suspended on one thread and resumed on another without consuming resources during suspension. This differs from threads, which can only be blocked but not suspended.
Here is an example of defining a suspend function within a coroutine for Android. If declared outside of a coroutine, the compiler will throw an error:
import kotlinx.coroutines.* fun main() { runBlocking { launch { fetchData() } } } suspend fun fetchData() { delay(2000) // Simulates a network or time-consuming operation println("Data fetched successfully!") }
In this example, the fetchData()
function is declared as a suspend function. It uses delay()
, a suspending function, to pause execution for 2 seconds without blocking the thread. The coroutine resumes execution once the delay is over.
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Do not call suspend function here. // Launch coroutine. GlobalScope.launch{ // Call suspend function here or from another suspend function only. } } }
Coroutine Example Code
Here is Kotlin coroutine code that performs an asynchronous network call using the async and await keywords:import kotlinx.coroutines.* import java.net.URL fun main() { // create a new coroutine scope runBlocking { // launch a new coroutine in the scope val job = launch { // create a new coroutine within the scope val result = async { // make an asynchronous network call URL("https://www.zyasin.com/jsonexample2.json").readText() } // wait for the result of the network call and print it println(result.await()) } // wait for the coroutine to complete job.join() } }
Complete code for a network call using Android coroutine can be found at github URL: Coroutine for Fetching JSON data.
Quiz Questions