Android from the foundation · Puzzle 15 of 16

Frames, jank and ANRs

Drawing the screen is just another message in the main thread's queue. So one slow message doesn't only delay a tap: it stops the screen from being drawn.

The problem

In puzzle 14, the main thread ran messages one at a time. Now suppose one message takes a long time, like reading a big file, or a Thread.sleep(6000) in a button's click code, which pauses the thread for 6 seconds. What does the user see?

See it

A timeline of frames every 16 ms; a slow message blocks the main thread, frames are missed, a touch waits, and after 5 seconds an ANR dialog appears kernel Binder your app system_server main thread a frame every 16 ms slow late: jank touch 5 s App isn't respondingANR
  1. The screen is redrawn 60 times a second; each redraw is a frame, due about every 16 milliseconds (ms). Each frame is a message in the main thread's queue.

    Step 1 of 4
  2. One message takes too long. The frames behind it can't be drawn in time, and the app stutters: jank.

    Step 2 of 4
  3. The user taps. system_server sends the touch, and it waits in the queue too. Predict: what if it waits much longer?

    Step 3 of 4
  4. system_server waits for your app to say, by Binder, that the touch is handled. After 5 seconds with no answer, it shows "App isn't responding": an ANR (Application Not Responding).

    Step 4 of 4

The building block

The screen is redrawn about 60 times a second. Each redraw is a frame, and each one must be ready within about 16 milliseconds (1000 ms ÷ 60). Drawing a frame is a message in the main thread's queue too.

If a message takes longer than that, the frames behind it come late, and the app visibly stutters: jank.

system_server sent each touch, and waits for your app to say it was handled. If a touch waits in the queue for 5 seconds with no answer, system_server shows the "App isn't responding" dialog: an ANR (Application Not Responding). That's why slow work (files, network, big calculations) belongs on another thread, never on the main thread.

The analogy

The clerk also has to repaint the shop window 60 times a second. One long phone call, and the window freezes; keep a customer waiting long enough, and the town hall puts up a sign: "not responding".

Check yourself

A button's click code calls Thread.sleep(6000), and the user taps the screen again right away. What happens?

Not quite. Touches are messages for the main thread too, so they wait behind the sleeping onCreate()Your override of this method is what the main thread calls when system_server says "create". 1864 protected void onCreate(@Nullable Bundle savedInstanceState) {Activity.java, line 1864 · AOSP frameworks/base ↗.

Right. One message at a time: the touch waits behind the sleep, and after 5 seconds comes the ANR.

Not quite. Nothing is stopped: system_server shows a dialog and the user decides.

A message takes 100 ms (about six frames' worth: 100 ÷ 16), and there are no touches. Is that an ANR?

Right. 100 ms ÷ 16 ms ≈ 6 frames late: jank. A touch waiting 5 s means an ANR.

Not quite. An ANR is about a touch waiting 5 seconds. 100 ms is jank: late frames.

Why should file reading go on another thread?

Not quite. It can; it just mustn't, because frames and touches wait behind it.

Right. Keep every main-thread message short.

Sources

What you now know

A frame is due about every 16 ms. A message that takes longer makes frames late: jank. A touch that waits 5 seconds gets the "App isn't responding" dialog: an ANR.