Skip to main content

Command Palette

Search for a command to run...

From Silicon to Screen

A Deep Dive into Android Threading, Jank, and ANRs

Updated
4 min read
From Silicon to Screen

As Android engineers, we spend most of our time in the application layer. However, the smoothness of our UI is governed by hardware limitations and how the Linux kernel manages resources. To build high-performance apps, we must understand the journey from a CPU instruction to a pixel being drawn.

The Hardware Foundation: CPUs, Cores, and Threads

At the lowest level, your Android device is powered by a Central Processing Unit (CPU). The CPU is the entire chip package (the "socket"). In the early days, a CPU had only one core. Today, a single CPU is essentially a cluster of cores sharing a few high-level resources Cache, Internal Bus etc.

  • Cores: A core is an independent processing unit within the CPU. Modern mobile SoCs (System on Chips) usually follow a "Heterogeneous" architecture (like ARM’s big.LITTLE), mixing high-performance cores with power-efficient cores. So today when mobile phone has 8 cores that means it’s CPU has 8 separate processing units to run instructions physically in parallel at same time.

  • Threads (Software): While a Core is hardware (silicon), a Thread is a software/OS abstraction (short for "thread of execution") is the smallest unit of programmed instructions that can be executed independently by an Operating System scheduler. At a time then there is possibility of one or more threads running on a single core due to multi threading (operated by OS with techniques like time slicing) & Hyper Threading (Achieved by Hardware)

Now let’s get back to Android,

The "Main Thread" (The UI Thread)

When your app process starts, the system creates the Main Thread. This is the most "expensive" thread in your app because it has a monopoly on the UI Toolkit.

The Main Thread's Responsibilities:

  1. Input Dispatching: Capturing touch events and key presses.

  2. UI Rendering: Executing the Measure, Layout, and Draw passes of your View hierarchy.

  3. Lifecycle Callbacks: Running onCreate, onStart, onResume, etc.

The Physics of Smoothness: 60 FPS and the 16ms Rule

Most mobile displays refresh at a rate of 60Hz (60 times per second). To provide a fluid experience, the Android system must generate a new frame every 16.66 milliseconds.

1000 milliseconds / 60 frame per second = 16.66 ms per frame

The Choreographer is the system component that coordinates this timing. It waits for a VSYNC signal from the display hardware and then tells the Main Thread: "You have 16ms to tell me what the next frame looks like."

When Things Go Wrong: Jank and ANR

If you give the Main Thread a task that takes too long, you hit two levels of failure:

Level 1: Jank (Dropped Frames)

If a task (like a heavy loop or complex layout calculation) takes 20ms, the Main Thread misses the 16.66ms deadline. The Choreographer cannot draw the frame on time, so the previous frame stays on the screen. The user perceives this as a "stutter" or Jank.

Level 2: ANR (Application Not Responding)

If the Main Thread is blocked for a significant period—typically 5 seconds for an input event—the system triggers an ANR. The OS assumes the app has hung and gives the user a dialog to force-quit.

The Solution: Background Threads

To protect the Main Thread, we offload "Heavy" or "Blocking" tasks to Background Threads (Worker Threads).

  • Blocking Operations: Networking (Retrofit), Database queries (Room), or heavy Image Processing.

  • Modern Implementation: In modern Android, we use Kotlin Coroutines with Dispatchers.IO (for I/O tasks) or Dispatchers.Default (for CPU-intensive calculations).

Summary Checklist for Android Engineers

  • Respect the 16ms budget: Keep onDraw and onLayout lean.

  • Offload to Background: If it involves a disk, a network, or a complex loop, it doesn't belong on the Main Thread.

  • Profile your app: Use the Android Studio Profiler to visualize thread activity and identify "Janky" frames.

By mastering hardware foundations and the Main Thread's role in UI rendering, developers can optimize app performance. Adhering to the 16ms frame budget and using background threads for heavy tasks prevent jank and ANR issues. Following these principles and using tools like the Android Studio Profiler ensures a seamless user experience.

For more dee dive, feel free to follow this holy grail!

More from this blog