For developers · Checked 1 October 2026

Android 17: lock-free MessageQueue and static final fields

Short answer: apps that target Android 17 (API level 37) get a new lock-free implementation of android.os.MessageQueue, which Google says improves performance and reduces missed frames but breaks code that reflects on its private fields and methods. The same target also makes static final fields unmodifiable: changing one through reflection throws IllegalAccessException, and through JNI it crashes the app. Both changes hit apps and libraries that rely on reflection, including older test frameworks.

What to update

From Android Developers’ Android 17 behavior changes and MessageQueue guidance, read 1 October 2026.

If you useWhat changes at target 37What to do
Reflection on MessageQueue, e.g. mMessagesmMessages stays but is always nullStop inspecting MessageQueue internals
EspressoOlder versions relied on MessageQueue reflectionUpdate to Espresso 3.7.0 or newer
RobolectricLegacy Looper mode is affectedUpdate to Robolectric 4.17 or newer; move LooperMode LEGACY to PAUSED
Reflection that writes static final fieldsThrows IllegalAccessExceptionStop writing to static final fields
JNI calls such as SetStaticLongField on a static final fieldThe app crashesRemove the write

Why MessageQueue changed

Since the first Android release, MessageQueue used a single lock to manage the main thread’s task queue, so a background thread could block the main thread and cause dropped frames. Android 17 rewrites the class underneath Looper and Handler without that lock. To keep binary compatibility the private mMessages field still exists, but in the new implementation it is always null, whether or not messages are queued, so any code that reads it to peek at pending messages silently gets the wrong answer.

Test frameworks

Espresso needs to know when the main thread is idle, and earlier versions found out through reflection. Espresso 3.7.0 and newer use the TestLooperManager API, including APIs added in Android 16, instead. If you run unit tests with Robolectric, update to Robolectric 4.17 or newer and migrate tests from @LooperMode(LEGACY) to @LooperMode(PAUSED), following Robolectric’s migration guide.

Testing and switching it off

You can try the new queue before changing your target SDK: on a debuggable build, run adb am compat enable USE_NEW_MESSAGEQUEUE <your-package-name>. Once you target API level 37 it is on by default. If you see crashes or odd behavior, turn it off temporarily with adb am compat disable USE_NEW_MESSAGEQUEUE <your-package-name> or in App Compatibility Changes in Developer Options, which restores the lock-based queue so you can confirm whether MessageQueue is the cause.

Other Android 17 changes

The full list is in Android 17 behavior changes. Raising your target to 37 brings more changes at once: certificate transparency by default, the local network permission and background audio hardening. For when Google Play will require it, see Google Play target API level.

Where Censuus fits

Fewer dropped frames keep users around; being found brings them. Censuus ranks apps by the visits they draw, and adding your app is free on the List my app form. Placement comes from traffic and sponsorship: apps climb on the visits they draw, and a sponsor can pay to rise higher.

Frequently asked questions

What changed in MessageQueue in Android 17?

Apps targeting Android 17 (API level 37) get a new lock-free implementation of android.os.MessageQueue. It improves performance and reduces missed frames but breaks code that reflects on MessageQueue private fields and methods.

Why is MessageQueue.mMessages null on Android 17?

The field is kept only for binary compatibility. In the new lock-free implementation it is always null, whether or not there are messages in the queue.

Which Espresso and Robolectric versions work with Android 17?

Espresso 3.7.0 or newer and Robolectric 4.17 or newer. Robolectric tests using LooperMode LEGACY need to move to LooperMode PAUSED.

How do I turn off the new MessageQueue for testing?

Run adb am compat disable USE_NEW_MESSAGEQUEUE with your package name, or use App Compatibility Changes in Developer Options.

Can I still change static final fields with reflection on Android 17?

No, if your app targets Android 17. Reflection throws IllegalAccessException, and modifying the field through JNI, such as SetStaticLongField, crashes the app.

Guides for app developers

Put your app in the ranking

Censuus ranks apps by the visits they draw and by sponsorship, no bots. Listing is free; sponsorship raises placement.