Android 16KB Page Sizes: Our Migration Rollercoaster

· 6 min read
androidmobile developmentreact nativegoogle playtechnical challengesapp optimization

We thought it would take a morning. It took three weeks.

So Google drops this announcement: "Hey everyone, starting November 1st, 2025, your apps need to support 16KB page sizes. Should be easy!"

We were migrating Arheev, our intelligent HR platform's mobile app. We read the docs. Seemed straightforward. Update a few build tools, add some config flags, maybe grab a coffee while it builds. Done by lunch, right?

Narrator voice: It was not done by lunch.

What Google Wanted (And Why)

The basic idea makes sense. Android has been using 4KB memory pages forever, but modern devices with tons of RAM work better with 16KB pages. Google's own data shows some nice improvements:

  • Apps launch 3% faster when your phone's memory is packed
  • 4.5% less battery drain during startup
  • Camera apps run 5–7% faster
  • Your phone boots 8% quicker

Cool benefits! So what's the catch?

The Real Problem Nobody Warned Us About

Here's where it gets spicy. Every native library (.so file) in your app needs to be "aligned" to 16KB boundaries. Think of it like parking: your library needs to fit neatly into 16KB sized parking spaces.

If you're writing a pure Java/Kotlin app, you're golden. No native code, no problem.

But the Arheev mobile app is built with React Native. And React Native apps are basically native libraries wrapped in JavaScript wrapped in more native libraries wrapped in existential questions about why we didn't just build separate native apps.

When libraries aren't properly aligned, you get:

  • Apps that refuse to install
  • Cryptic error messages that tell you nothing
  • Crashes that only happen on specific devices (usually the ones your boss is testing on)

Fun times!

What Actually Happened: A Timeline

Day 1: "This Will Be Easy"

We updated to Android Gradle Plugin 8.5.1 and NDK r28. The docs said these versions handle 16KB alignment automatically. We added some flags to AndroidManifest.xml:

<application
  android:enableOnBackInvokedCallback="true"
  android:extractNativeLibs="true">

  <property
    android:name="android.app.16kb_page_size"
    android:value="true" />
</application>

Build passed. Tests green. High fives all around. Time to celebrate our sub one hour migration!

Day 3: "Why Isn't This Working?"

We spun up an Android 15 emulator with 16KB support enabled. Installed the app. It crashed immediately.

Turns out, several libraries hiding in our dependency tree weren't 16KB aligned:

  • Our audio processing library (libandroidlame.so)
  • Some image compression thing
  • A third party SDK we barely remembered adding

The error message? "Native module initialization failed." Thanks, Android. Super helpful.

We spent two days learning more about ELF alignment than we ever wanted to know.

Day 5: "Fine, We'll Work Around It"

After much Googling (and maybe some coffee fueled Stack Overflow diving), we found the magic flag: android:extractNativeLibs="true".

This tells Android to extract libraries when installing the app instead of loading them directly from the APK. It basically sidesteps the alignment requirement entirely.

Is it perfect? No. Does it work? Yes. Are we shipping it? Absolutely.

The Good:

  • Fixed our crashes instantly
  • Works with any library, aligned or not
  • No waiting for third party developers to update their stuff

The Not So Good:

  • Apps take a bit longer to install
  • Uses more storage on the device
  • Google might phase this out eventually (but that's future us's problem)

We're treating this as a temporary fix while we work with library maintainers to get proper 16KB aligned versions.

Day 7: "Let's Automate This Before We Forget"

The last thing we wanted was for someone to run expo prebuild and accidentally wipe all our hard earned configurations.

So we built an Expo config plugin that handles everything automatically:

AndroidManifest.xml changes:

  • Adds the 16KB page size property
  • Enables the back gesture callback
  • Sets the extractNativeLibs flag

build.gradle updates:

  • Configures NDK ABI filters
  • Enables multidex support
  • Sets up release signing

gradle.properties tweaks:

  • Build tool settings
  • React Native new architecture flags
  • Resource optimization

Now when anyone on the team runs prebuild, all these settings just... work. No more "wait, where did that config go?" moments.

Things We Wish We Knew From Day One

1. Upgrade Everything First

Don't even think about starting this migration without Android Gradle Plugin 8.5.1+ and NDK r28+. Older versions just don't handle 16KB alignment properly.

Update your android/build.gradle:

dependencies {
  classpath('com.android.tools.build:gradle:8.5.1')
}

Yes, it's annoying to update build tools. Do it anyway.

2. Find Out If You Even Have Native Code

Before you panic, figure out if your app actually uses native libraries. The easiest way? Use APK Analyzer in Android Studio:

  1. Open Android Studio (any project works)
  2. Click Build > Analyze APK...
  3. Select your APK file
  4. Look for a lib folder

If you see a lib folder with .so files inside, congrats; you have native code to deal with. Check the Alignment column for any warning messages.

APK Analyzer menu in Android Studio

No lib folder? You're using pure Java/Kotlin and can probably skip most of this headache. Lucky you.

3. Actually Test on a 16KB Emulator

Create an Android 15 emulator in AVD Manager and enable "16KB page size" in the advanced settings. Your regular device won't catch these issues because it's still using 4KB pages.

Trust us, you don't want to discover alignment problems after shipping to production.

4. Hunt Down Your Native Libraries

Find all those .so files lurking in your app:

# List all native libraries in your APK
unzip -l app-release.apk | grep '\.so$'

Or just use APK Analyzer again; it's way easier and has a nice UI.

Make a list. Check if any are causing problems. Curse at the ones that are.

5. Keep Tabs on Your Dependencies

Not every library maintainer has jumped on the 16KB bandwagon yet. Some haven't even heard of it. For React Native:

  • Check if your Expo SDK version has the fixes
  • Browse GitHub issues for your native modules
  • Consider switching libraries if the maintainer went AWOL

6. The extractNativeLibs Trick Is Temporary

Yeah, android:extractNativeLibs="true" fixes everything right now. But treat it like duct tape: it works, but you'll want a proper fix eventually. Keep track of which libraries need it and watch for updates.

Your Migration Checklist (So You Don't Miss Anything)

Here's what you actually need to do:

  • Update AGP to 8.5.1+ and NDK to r28+
  • Update Gradle to 8.14+ (might as well)
  • Use APK Analyzer to check if you have native libraries
  • Add the 16KB page size flags to AndroidManifest.xml
  • Set up NDK ABI filters in build.gradle
  • Add extractNativeLibs if you're hitting crashes
  • Create an Android 15 emulator with 16KB enabled
  • Actually test everything (don't just assume it works)
  • Make a list of problematic libraries
  • Automate your config (future you will thank you)

The Reality of Cross Platform Development

Here's the thing about platform requirements like this: they don't just affect your code. They affect every single library in your dependency tree.

When Google says "support 16KB," every native module developer, every SDK provider, every community package needs to comply. And your migration is only done when the slowest library finally catches up.

React Native makes this extra fun because you're not just waiting on one ecosystem; you're waiting on native iOS developers, Android developers, and JavaScript developers all coordinating. It's like herding cats, except the cats are also juggling.

Was It Worth It?

After all that work, did we actually see improvements?

Yeah, we did:

  • Less memory fragmentation on newer devices
  • Slightly faster app starts (hard to measure exactly, but it's there)
  • No more "your app doesn't work on my phone" bug reports from Android 15 users

The extractNativeLibs workaround makes installation a bit slower, but once the app's running, everything feels fine.

What's Still Not Perfect

We're still using extractNativeLibs="true" instead of properly aligned libraries. This works, but it's technically a workaround, not a solution.

A few dependencies still haven't released 16KB-aligned versions. We're watching their GitHub issues, but some haven't been updated in months. Might need to fork them eventually or find alternatives.

And we're not 100% sure how this will perform on actual 16KB devices once they're common. Emulators are one thing, real hardware is another. We'll find out.

What's Next?

More of the same, probably. As Android phones get more powerful, platform requirements will keep cascading through dependency trees. The automation we built for this migration will get used again on the next one.

The Bottom Line

Migrating to 16KB page size support isn't optional if you want to ship on Google Play. And it's definitely more involved than the documentation makes it sound.

But once you get through it, you're good. The config mostly runs itself, your app performs better on newer devices, and you have one less deadline to stress about.

If you're in the middle of this migration right now and running into weird issues, you're not alone. We've been there. Feel free to reach out if you want to commiserate about library alignment or swap war stories about cryptic Android error messages.

Unlike most migrations, this one actually has a deadline. But at least you don't have to do it alone; every React Native developer is going through the same alignment nightmares.

Android 16KB Page Sizes: Our Migration Rollercoaster