KhueApps
Home/React Native/React Native: Complying with Google Play’s 16kB page size policy

React Native: Complying with Google Play’s 16kB page size policy

Last updated: October 07, 2025

What the 16kB page size policy means

Google Play requires apps that ship native code (.so) to run correctly on devices that use a 16kB memory page size. If your APK/AAB assumes 4kB pages, loading native libraries can fail on those devices. Compliance boils down to:

  • Build native libraries that don’t assume 4kB pages (linker page-size alignment).
  • Package libraries so the loader can map them on 16kB devices (page-aligned, uncompressed when applicable).

How it affects React Native apps

React Native apps often include native libraries (Hermes/JSC, RN core, and third-party modules). Even if your app is mostly JavaScript, the APK likely contains .so files under lib/<abi>/. You must ensure these libraries:

  • Are linked with a max page size compatible with 16kB devices.
  • Are packaged in a way the system can load directly (or safely extracted on older OS versions).

Quickstart

  1. Use a recent Android Gradle Plugin (AGP) and NDK (r23+ recommended).
  2. If publishing an AAB, enable uncompressed native libs so Play aligns them appropriately.
  3. Ensure packaging doesn’t force legacy extraction on devices that support direct loading.
  4. Link your native code with a 16kB max page size.
  5. Verify with readelf and zipalign.

What you change and where

ChangePurposeWhere
Use NDK r23+Modern linker defaults, better 16k supportandroid/ndkVersion or local NDK
Enable uncompressed native libsAllows direct mapping and proper page alignmentgradle.properties (AAB)
Disable legacy JNI packaging (API 23+)Keep libs uncompressed in APKsapp/build.gradle
Link with -Wl,-z,max-page-size=16384Produce 16k-compatible .soCMakeLists.txt or ndkBuild
Verify alignmentCatch misaligned libs earlyCI shell script

Minimal working example

The snippets below show a minimal RN Android setup that complies. Adjust names/paths to your project.

gradle.properties

# Ensures Play delivers uncompressed, page-aligned native libs from AAB
android.bundle.enableUncompressedNativeLibs=true

app/build.gradle (Groovy)

android {
    defaultConfig {
        // Prefer minSdk 23+ to fully benefit from direct loading
        minSdkVersion 23
        ndk {
            abiFilters "arm64-v8a", "armeabi-v7a", "x86_64"
        }
    }

    // For AGP 7.x
    packagingOptions {
        jniLibs {
            // false = uncompressed, page-aligned JNI libs on API 23+
            useLegacyPackaging = false
        }
    }

    // If you build a native module via CMake
    externalNativeBuild {
        cmake {
            path file("src/main/cpp/CMakeLists.txt")
        }
    }
}

AndroidManifest.xml (application tag)

<application
    android:name=".MainApplication"
    android:extractNativeLibs="false">
    <!-- ... -->
</application>

CMakeLists.txt (example native module)

cmake_minimum_required(VERSION 3.18)
project(reactnative16k)

add_library(mylib SHARED native-lib.cpp)

# Critical: ensure segments are compatible with 16k page size
# Works with LLD from NDK r23+
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|armv7|x86_64")
    target_link_options(mylib PRIVATE "-Wl,-z,max-page-size=16384")
endif()

target_link_libraries(mylib log)

src/main/cpp/native-lib.cpp

#include <jni.h>

extern "C" JNIEXPORT jint JNICALL
Java_com_example_MainActivity_nativeAdd(JNIEnv*, jobject, jint a, jint b) {
  return a + b;
}

Verification

Run these checks on the final artifact you will ship.

  1. Verify ELF segment alignment (each .so):
readelf -l app/build/intermediates/merged_native_libs/release/out/lib/arm64-v8a/libmylib.so | grep -E "LOAD|Align"

Look for Align values that are >= 0x4000 (16384) for PT_LOAD segments.

  1. Verify APK page alignment (for direct loading):
# Build a release APK or extract an APK from your AAB using bundletool, then:
zipalign -c 16384 app-release.apk

Expect “Verification successful”. If it fails, your libs aren’t aligned to 16k pages.

  1. Check for uncompressed native libraries in the APK:
unzip -l app-release.apk | grep "^\s*0\s*lib/.*\.so$" || true

A size of 0 in the compressed-size column indicates uncompressed entries.

  1. Play pre-launch report Upload to an internal testing track and confirm no “16kB page size” warnings.

Step-by-step (recommended path for RN apps)

  1. Upgrade toolchain
  • Use AGP 7.0+ and Gradle compatible with your RN version.
  • Set ndkVersion in android/build.gradle or install NDK r23+ via SDK Manager.
  1. Enable uncompressed native libs (AAB)
  • In gradle.properties: android.bundle.enableUncompressedNativeLibs=true
  • Publish AAB to Play; it will deliver page-aligned, uncompressed libs to API 23+ devices.
  1. Disable legacy packaging on API 23+
  • app/build.gradle: packagingOptions.jniLibs.useLegacyPackaging = false
  • Manifest: android:extractNativeLibs="false"
  1. Rebuild native modules
  • If you maintain native code, add -Wl,-z,max-page-size=16384.
  • For ndk-build, add to LOCAL_LDFLAGS; for CMake, use target_link_options.
  1. Audit third-party AARs
  • If a vendor ships prebuilt .so with 4k-only alignment, request updated artifacts or recompile from source.
  1. Verify artifacts
  • Use readelf on .so files and zipalign on the APK.
  • Test on a device/emulator fleet if possible.
  1. Ship and monitor
  • Upload to internal test track; check Play Console warnings.

Pitfalls and fixes

  • Old toolchains: NDK < r21 or custom linkers may produce 4k-only segments. Fix by upgrading NDK and adding the linker flag.
  • minSdk < 23: Direct loading of uncompressed libs is limited on older OS versions. You can still link with 16k max page size and allow extraction on API < 23; keep useLegacyPackaging=true for those builds if needed.
  • Debug vs release differences: Debug APKs might be packaged differently. Always verify the release artifact.
  • Mixed ABI settings: Ensure all ABIs you ship are rebuilt with the new flags.
  • Prebuilt third-party .so: These are the most common source of failures. Audit and replace if necessary.

Performance notes

  • Uncompressed, page-aligned native libs allow the system to mmap sections directly, reducing install time and storage overhead.
  • 16k alignment can add small padding to the APK; expect a negligible size increase.
  • Faster cold start is typical when avoiding extraction on API 23+.

FAQ

  • Do pure-JS React Native apps need this? If your APK has no .so files, you’re unaffected. Most RN apps include .so (e.g., Hermes/JSC), so check.
  • Is AAB required? Strongly recommended. Play can optimize delivery (including aligning libs) from AABs.
  • Do I still need the linker flag if Play aligns my APK? Yes. Alignment at packaging time doesn’t fix ELF segment alignment set at link time.
  • Which ABIs are impacted? Primarily arm64-v8a; apply changes to all ABIs you ship.
  • How do I detect failing libs? Use readelf on each .so; check PT_LOAD Align values and rebuild offenders.
  • Will this break older Android versions? No. Properly linked libs work across page sizes. For API < 23, you may rely on extraction if needed.

Series: React Native Intermediate tutorials

React Native