Surviving Android’s 16KB Page Size Mandate: A React Native War Story
How we migrated a production React Native app to 16KB page alignment, swapped out a deprecated camera library, and lived to tell the tale.
Surviving Android’s 16KB Page Size Mandate: A React Native War Story
How we migrated a production React Native app to 16KB page alignment, swapped out a deprecated camera library, and lived to tell the tale.

TL;DR
Starting with Android 15, Google requires every .so file in your APK/AAB to be aligned to 16KB page boundaries instead of the historical 4KB. What sounded like a one-line Gradle flag became a multi-week journey: NDK 29 upgrades, ML Kit API breakages, a complete camera library migration, a custom Kotlin patch to merge CameraX surfaces, and a 100MB jump in APK size.
If your app ships native camera, ML Kit, or older third-party libraries — this post is for you.
Why 16KB Page Size Matters
Modern ARM64 kernels can use either 4KB or 16KB memory pages. Android historically standardized on 4KB, but newer devices (Pixel 8+, OnePlus, Samsung flagships) ship with 16KB-page kernels.
The catch: native libraries (.so files) must match the host kernel page size. A 4KB-aligned .so crashes at load time on 16KB devices.
Google’s enforcement timeline:
- Android 15 (API 35): 16KB devices become mainstream.
- Nov 2025: Play Store submissions targeting SDK 35+ must ship 16KB-aligned libraries.
- Non-compliant apps crash or are rejected.
No exceptions.
The Starting Point
Our app:
- React Native 0.79.1
- Camera via
react-native-camera@4.2.1(deprecated) - ML Kit for barcode, face detection, OCR
- ~30 native libraries across 4 ABIs
What we expected: flip a Gradle flag, ship. Reality: 8 commits, 3 branches, a custom Kotlin patch, full camera library replacement.
Phase 1 — The “Easy” Gradle Flips
# android/gradle.properties
android.experimental.enablePageSizeAlignment=true
// android/app/build.gradle
packaging {
jniLibs {
useLegacyPackaging = false
}
}
- Align
.sofiles to 16KB boundaries. - Store
.souncompressed for directmmap. - Upgrade toolchain: AGP 8.13.0, NDK 29, compileSdk 36.
Result: app still crashed on devices.
Phase 2 — ML Kit Breakages
react-native-camera broke in three ways:
- NoClassDefFoundError for
Detector→ upgraded ML Kit, updated imports. - Barcode class moved → patched 3 Java files.
- NoSuchMethodError due to forced vision-common version → reverted force directives, set app-level dependencies.
Bonus: some .so files not 16KB-aligned. Excluded from build to comply.
Phase 3 — Camera Library Migration
Legacy Camera1 API broke on Android 15+ → black preview screens.
Solution: migrate to **react-native-vision-camera** (Camera2/CameraX).
- Handles modern Android correctly
- Supports frame processors for OCR and barcode scanning
<Camera
isActive={isCameraOn && isForeground}
codeScanner={codeScanner}
frameProcessor={frameProcessor}
/>
Key detail: isForeground prevents session leaks.
Phase 4 — Two-Surface Problem
Budget devices only support 2 concurrent surfaces (Preview + Analyzer).

Solution: Combined Analyzer Kotlin patch merges OCR + barcode into one surface.
- Prevents black screens
- Handles refCount correctly to avoid silent failures
Phase 5 — NDK 29 Crashes
libVisionCamera.so failed due to __cxa_init_primary_exception.
Solution: copy NDK 29 libc++_shared.so into jniLibs with preBuild Gradle task to guarantee proper symbol resolution.
OCR Gotchas
- Regex failures after whitespace stripping
Date.now()reset on re-renders → useuseRef- JS throttling only; never throttle native frame processor
APK Size Jump
Metric Before After
------------------------------------------------
Release APK ~120 MB ~225 MB
.so files per ABI 23 27
Total native libs ~100 MB ~180 MB

Mitigations: ship as AAB, ABI splits, unbundled ML Kit.
Lessons Learned
- Gradle flag is easy; dependencies are not.
- Don’t force ML Kit globally; use app-level implementation.
- Camera1 is deprecated; migrate early.
- Test on budget devices (2-surface limit matters).
- Refcount lifecycles are unforgiving.
- NDK upgrades break ABI assumptions.
- APK size matters; optimize with AAB and ABI splits.
TL;DR
- All
.sofiles now 16KB-aligned - ML Kit upgraded and patched
- Camera library migrated to VisionCamera
- Combined Analyzer patch solves 2-surface issues
- Gradle + NDK + packaging fully compliant
Your app is future-proof for Android 15+.
메타데이터
- post_id
- 51ed7d3e59cc
- slug
- surviving-androids-16kb-page-size-mandate-a-react-native-war-story-51ed7d3e59cc
- url
- https://medium.com/@tusharbhogade/surviving-androids-16kb-page-size-mandate-a-react-native-war-story-51ed7d3e59cc
- canonical_url
- https://medium.com/@tusharbhogade/surviving-androids-16kb-page-size-mandate-a-react-native-war-story-51ed7d3e59cc
- author_url
- https://medium.com/@tusharbhogade
- status
- ok
- fetched_at
- 2026-06-09 15:37:30