Our check of five APKs: VLC 3.5.4 arm64 and x86_64 libraries UNALIGNED at 4096, VLC 3.7.2 and Organic Maps ALIGNED at 16384, while zipalign -P 16 says Verification succesful for all five

Google Play has required apps that target Android 15 (API level 35) or higher to support 16 KB memory pages on 64-bit devices since November 2025, with extensions handed out in Play Console. The extensions are ending: Android's developer guide now says that starting 1 February 2027, if your app updates don't support 16 KB page sizes, you won't be able to release them. New apps and updates have had to target API 36 since 31 August 2026 (1 November with an extension), so the 16 KB rule now covers every app that is still being updated.

This is how to tell whether your APK is ready, including a check that passes when it shouldn't, our own test of real APKs, and what to upgrade in React Native, Expo and Flutter projects.

Does it affect your app?

Only native code is affected: any .so file in the APK's lib/ folder, whether you wrote C/C++, pulled in an SDK that ships one, or use a framework that does. Pure Java and Kotlin apps already work on 16 KB devices. In practice that means almost every cross-platform app is affected: React Native ships Hermes and its own native libraries, Flutter ships libflutter.so, and many popular SDKs (image, video, database, crypto) carry native code too. Unzip your APK and look at lib/arm64-v8a/: if there's anything there, read on.

Why Google is doing it: in Google's own testing, 16 KB devices launched apps 3.16% faster on average under memory pressure (up to 30% for some apps), drew 4.56% less power during app launch, started the camera 4.48% faster hot and 6.60% faster cold, and booted about 8% (roughly 950 ms) faster.

Our test: three apps, five APKs

We took real, published APKs from F-Droid and checked every native library: the alignment of each ELF LOAD segment (it must be 16384, written 0x4000 or 2**14), and, for libraries stored uncompressed, whether the file starts on a 16 KB boundary inside the zip. Only 64-bit libraries count for the rule; 32-bit armeabi-v7a and x86 libraries are listed but not judged.

text
APK (F-Droid, 23 Oct 2026)            ABI          libraries   LOAD align   verdict
VLC 3.5.4 (code 13050407)             arm64-v8a        4          4096        UNALIGNED (all 4)
VLC 3.5.4 (code 13050408)             x86_64           4          4096        UNALIGNED (all 4)
VLC 3.7.2 (code 13070206)             arm64-v8a        4         16384        ALIGNED
VLC 3.7.2 (code 13070208)             x86_64           4         16384        ALIGNED
Organic Maps 2026.09.29               arm64-v8a        1         16384        ALIGNED
                                      x86_64           1         16384        ALIGNED
                                      armeabi-v7a, x86 1 each     4096        (32-bit, rule n/a)

libraries in VLC: libvlc.so (42.4 MB in 3.5.4), libmla.so, libc++_shared.so, libvlcjni.so
all libraries in all five APKs are stored compressed (deflated)

The old VLC is what a not-yet-migrated app looks like: every 64-bit library linked with 4 KB segments. The new one was rebuilt and every segment moved to 16 KB. llvm-readelf from the NDK shows the difference directly:

text
$ llvm-readelf -Wl lib/arm64-v8a/libvlcjni.so        # VLC 3.5.4
  LOAD  0x000000 0x0000000000000000 ... 0x012828 0x012828 R E 0x1000
  LOAD  0x013750 0x0000000000014750 ... 0x0008b0 0x000a18 RW  0x1000

$ llvm-readelf -Wl lib/arm64-v8a/libvlcjni.so        # VLC 3.7.2
  LOAD  0x000000 0x0000000000000000 ... 0x016cb0 0x016cb0 R E 0x4000
  LOAD  0x016cb0 0x000000000001acb0 ... 0x000898 0x001350 RW  0x4000
  LOAD  0x017548 0x000000000001f548 ... 0x000000 0x0001b0 RW  0x4000

The check that passes when it shouldn't

Many guides tell you to run zipalign's 16 KB check and look for "Verification successful". We ran it on all five APKs:

text
$ zipalign -c -P 16 -v 4 org.videolan.vlc_13050407.apk     # 4 KB libraries
...
Verification succesful
$ zipalign -c -P 16 -v 4 org.videolan.vlc_13070206.apk     # 16 KB libraries
...
Verification succesful

(build-tools 35.0.0; all five APKs pass, including both unaligned ones)

It passes the unaligned VLC because zipalign checks where files sit inside the zip, not how the libraries inside were linked. Its -P 16 option only matters for libraries stored uncompressed (the Android Gradle Plugin's default for apps with minSdk 23 or higher), which the system maps straight out of the APK. These F-Droid builds compress their libraries, so there is nothing for zipalign to check, and it says yes. The two checks answer different questions, and you need both:

text
check                         question it answers                         tool
ELF LOAD segment alignment     were the .so files linked for 16 KB pages?  llvm-readelf / llvm-objdump,
                                                                           check_elf_alignment.sh,
                                                                           Android Studio APK Analyzer
zip alignment of stored libs   can the system map them from the APK?      zipalign -c -P 16 -v 4

A checker you can run anywhere

check_elf_alignment.sh from Google needs Linux or macOS and the NDK. This Python script (standard library only, Windows too) does both checks for every library in an APK; it's what produced our table:

python
import struct, sys, zipfile

def load_align(data):
    b64 = data[4] == 2
    phoff, = struct.unpack_from("<Q" if b64 else "<I", data, 0x20 if b64 else 0x1c)
    size, count = struct.unpack_from("<HH", data, 0x36 if b64 else 0x2a)
    aligns = [struct.unpack_from("<Q" if b64 else "<I", data, phoff + i * size + (48 if b64 else 28))[0]
              for i in range(count) if struct.unpack_from("<I", data, phoff + i * size)[0] == 1]
    return min(aligns), b64

apk = zipfile.ZipFile(sys.argv[1]); raw = open(sys.argv[1], "rb")
for e in apk.infolist():
    if not (e.filename.startswith("lib/") and e.filename.endswith(".so")):
        continue
    raw.seek(e.header_offset + 26); n, m = struct.unpack("<HH", raw.read(4))
    offset = e.header_offset + 30 + n + m          # where the file's bytes start
    stored = e.compress_type == 0
    align, b64 = load_align(apk.read(e))
    ok = align >= 16384 and (not stored or offset % 16384 == 0)
    print(e.filename, align, "stored" if stored else "deflated",
          ("ALIGNED" if ok else "UNALIGNED") if b64 else "32-bit, not checked")

Run it on the APK you actually ship. For an app bundle, build the APKs from it first with bundletool (build-apks, then unzip the arm64 split): Google notes that with AGP 8.3 to 8.5, bundletool doesn't zipalign APKs by default, so an app can look fine locally and fail to install when Play builds it from the bundle.

No Python to hand? The same two checks run in the browser on our 16 KB page size checker: drop an APK or AAB and it lists every native library with its LOAD alignment, where it sits in the zip and a verdict. The file is read on your computer and never uploaded, so a 200 MB release build is fine.

The aipps.dev 16 KB checker on VLC 3.5.4 (org.videolan.vlc_13050407.apk, 34.5 MB): not ready, 4 of 4 64-bit libraries UNALIGNED, libc++_shared.so, libmla.so and libvlc.so in arm64-v8a with LOAD align 4096 (0x1000), compressed
The old VLC build in aipps.dev/tools/16kb-check: all four arm64 libraries linked at 4096, the same result as the script above.

How to fix it

text
toolchain           version that gets you there                                 notes
Android Gradle      AGP 8.5.1+                                                  16 KB zip alignment of
Plugin                                                                         uncompressed libraries
NDK                 r28+ (aligned by default)                                  r27 and older: add
                                                                               -Wl,-z,max-page-size=16384
                                                                               -Wl,-z,common-page-size=16384
React Native        0.77+ (released 21 January 2025)                           16 KB-ready native libraries
Expo                SDK 53+ (React Native 0.79)                                 SDK 52 is RN 0.76; upgrade
Flutter             a current stable release, NDK r28                          older engines (3.13 reported)
                                                                               flagged libflutter.so
prebuilt .so files  rebuild them, or get 16 KB builds from the vendor          the most common holdout

The framework upgrade is usually the smaller part. The holdouts are third-party native SDKs that you don't compile: an analytics or payments SDK pinned to an old version, or a .so committed to the repo years ago. Run the checker, list every UNALIGNED library, and map each one to the dependency that brings it in. If you can't upgrade the AGP, Google's fallback is useLegacyPackaging = true under packagingOptions jniLibs, which compresses the libraries (as F-Droid's builds above do): it removes the zip-alignment question but not the ELF one, and makes the installed app bigger.

Testing on a 16 KB device

text
emulator   Android Studio > SDK Manager > SDK Platforms > Show Package Details,
           Android 15 (VanillaIceCream) or higher:
           "Google APIs Experimental 16 KB Page Size ARM 64 v8a System Image"
           (or the Intel x86_64 Atom one)
phone      Developer options > "Boot with 16KB page size" on Pixel 8, 8 Pro, 8a
           (Android 15 QPR1+), Pixel 9 series (QPR2+), Pixel 9a (Android 16+)
check      adb shell getconf PAGE_SIZE      ->  16384

Install the build, open every screen that loads native code (camera, video, maps, database, anything with a native SDK) and watch logcat for dlopen errors. On Android 16, unaligned apps may run in a compatibility mode with a warning on launch; don't take a launch that works as proof, the checker is.

aipps.dev builds your React Native or Android app on a current NDK and AGP, and every build gets a QR install page so the 16 KB test phone can install it in seconds.

See aipps.dev

Related: the 2026 store deadlines calendar puts this date next to the API 36 and Xcode 26 requirements, and what iOS and Android builds cost on CI in 2026 for the machines that have to run the newer toolchain.

Sources: Android Developers, "Support 16 KB page sizes" (Play requirement, 1 February 2027 date for updates, AGP and NDK versions, linker flags, test devices, performance figures), read 23 October 2026; Android Developers, target API level requirements; React Native 0.77 release (21 January 2025); flutter/flutter issues 150168 and 175599. Test: F-Droid APKs org.videolan.vlc 13050407, 13050408, 13070206, 13070208 and app.organicmaps 26092932, downloaded 23 October 2026; zipalign from build-tools 35.0.0, llvm-readelf from NDK 27.1, and the script above.