Threads in Android

What is Threading in Android?

Threading in Android refers to executing multiple tasks simultaneously to improve app performance and responsiveness. The threading is required because the main UI thread (a.k.a. Main Thread) handles user interactions and UI updates. If heavy tasks (e.g., network calls, database operations) run on the UI thread, the app freezes (ANR: “App Not Responding”). To avoid this, long-running operations should run in background threads.

In Android development, threads are used to manage how an app performs tasks concurrently. Threads allow your app to perform multiple operations at the same time, without freezing or blocking the user interface (UI). There are several types of threads in Android, each serving a specific purpose in app development. Let’s dive deeper into the types of threads and when to use them.

Types of threads

Main thread, Worker Threads (Background Threads), Java Threads (Thread class), Handler & Looper, AsyncTask (Deprecated), Executors, HandlerThread, RxJava & Coroutines (Advanced).

Main Thread (UI Thread)

The Main Thread, also known as the UI Thread, is the default thread that runs when an Android application starts. It is the thread responsible for interacting with the user interface, handling events, and updating the UI. This thread is crucial because it ensures the app responds to user input, like button clicks, touch gestures, animations, and screen updates. In fact, only the main thread is allowed to interact directly with the UI components, such as updating a TextView or showing a Toast.

However, one major limitation of the main thread is that it should never be used for long-running or blocking tasks. This is because any task that takes a significant amount of time (such as making a network request, reading from a database, or performing intensive calculations) will block the UI thread, causing the app to become unresponsive. If this happens, the system may display an Application Not Responding (ANR) dialog to the user, indicating that the app has become unresponsive.

Worker Threads (Background Threads)

When performing long-running or computationally expensive tasks in an Android app, you should use background threads. Background threads allow you to offload work from the main thread so that the UI remains responsive. There are several types of background threads in Android, each with its own advantages and use cases.

Java Threads (Thread class)

The Thread class is the most basic way to create and manage a background thread in Android. It allows you to run any code asynchronously in a separate thread from the main UI thread. You can extend the Thread class or implement the Runnable interface and then call the start() method to begin executing the code on the new thread.

While using the Thread class directly is straightforward, it has a few downsides:

  • It requires manual management of thread creation and lifecycle.
  • It doesn’t provide easy mechanisms for handling results or interacting with the UI thread.

For example, if you need to update the UI after the task is finished, you’ll need to use a mechanism like a Handler or runOnUiThread().

Handler & Looper

A Handler is a utility class in Android that allows you to send and process messages or run code on a specific thread’s message queue. Every thread in Android has a message queue, and the main thread uses the Looper to process the messages in this queue. When you want to perform work on a background thread and then update the UI, you can use a Handler associated with the main thread’s Looper.

Here’s how it works:

  • A Handler is associated with a specific Looper (usually the main thread’s Looper).
  • You can use the Handler to post messages or runnable tasks to the main thread’s message queue, which will then be processed by the Looper.

This is useful for background tasks that need to interact with the UI. For example, a background thread might process some data and then send the result to the main thread using a Handler to update a TextView.

AsyncTask (Deprecated)

The AsyncTask class was once a convenient way to perform background operations in Android. It provides a simple way to execute tasks asynchronously on a background thread and then update the UI after completion. An AsyncTask has three generic parameters:

  1. Params (the type of the input for the background task),
  2. Progress (the type of progress information),
  3. Result (the type of result returned by the task).

An AsyncTask is typically used for short operations, such as downloading a file or processing data in the background. It provides methods like onPreExecute(), doInBackground(), and onPostExecute() for handling the lifecycle of the task.

However, AsyncTask has been deprecated in Android API level 30 (Android 11), mainly due to its limitations:

  • It doesn’t handle configuration changes (such as screen rotations) well.
  • It leads to memory leaks if not used properly.
  • It’s more difficult to manage when the background tasks become more complex.

As a result, developers are encouraged to use other alternatives for background tasks.

Executors

Executors are part of the Java concurrency framework and offer a more robust and flexible way to handle background tasks compared to Thread. Executors are used to manage a pool of threads and execute tasks asynchronously. Android provides a ExecutorService implementation that can help you execute tasks in a thread pool. This makes it easier to manage multiple tasks efficiently, especially when you need to handle a large number of concurrent operations.

For example, an ExecutorService can manage a pool of threads that execute background tasks, allowing you to avoid the overhead of creating and managing individual threads manually. Executors also provide better control over thread lifecycle and task queuing, making them a better choice for complex background operations.

HandlerThread

A HandlerThread is a subclass of Thread that provides a looper for its own message queue. This class is useful when you want to run a background thread that can handle messages or tasks sent to it. The HandlerThread creates a looper and automatically manages its lifecycle, making it a convenient way to handle background tasks that require message handling.

For example, if you have a background task that needs to continuously process events (like downloading files), a HandlerThread provides a convenient way to manage that task and interact with it through a Handler.

RxJava & Coroutines (Advanced)

RxJava and Coroutines are modern tools for handling asynchronous tasks in Android, especially for more complex or reactive programming scenarios.

  • RxJava: This is a library for reactive programming that allows you to manage asynchronous operations using the observer pattern. It helps you compose asynchronous tasks in a more declarative way. With RxJava, you can chain operations and handle results, errors, and events in a reactive stream.
  • Coroutines: Coroutines are a Kotlin-based way of managing asynchronous operations. They provide a more lightweight and easier-to-read way of handling concurrency. Coroutines simplify asynchronous code by allowing you to write it in a sequential style, even though it runs in the background. Unlike threads, coroutines are lightweight and efficient, making them a great option for tasks that involve a lot of asynchronous operations.

Both RxJava and Coroutines offer a more powerful and flexible way of handling complex background operations compared to older methods like AsyncTask or Thread. They allow developers to write non-blocking, asynchronous code in a more maintainable way, especially when tasks depend on each other or need to interact with multiple data streams.

When to Use Which Threading Approach?

Approach Use Case
Thread Simple background tasks
Handler & Looper Communicating between threads
AsyncTask (Deprecated) Small tasks needing UI updates
Executors Best for managing multiple threads efficiently
HandlerThread Long-running background processing
Kotlin Coroutines Modern, simple threading for Kotlin

Conclusion

In Android, the main thread is responsible for interacting with the UI, while background threads handle long-running tasks. To prevent blocking the UI, background threads are essential for operations like network requests, database access, and heavy computations. The choice of which background thread to use depends on the complexity of the task and the needs of the app. For simpler tasks, Thread, Handler, or AsyncTask (though deprecated) may suffice. For more complex tasks, or when dealing with large numbers of concurrent operations, Executors, HandlerThread, RxJava, or Coroutines are better suited. By understanding the differences between these thread types, developers can build more efficient, responsive Android applications.