-
Set JDK 17 and ANDROID_HOME before Gradle: React Native 0.86 / AGP 8.x expect JDK 17. Point ANDROID_HOME at the Android SDK and add platform-tools to PATH - cryptic Gradle failures usually trace to toolchain drift.
-
Use npx expo run:android for daily native loops: Android Studio Run ▶ does not wire Metro the way Expo dev clients expect. Terminal-driven builds match how EAS and CI compile.
-
Treat android/ as generated in CNG: Edit app.config.ts and config plugins, not hand-merge build.gradle on every SDK bump. Run npx expo prebuild --clean only when native config actually changed.
-
Know the Gradle map: settings.gradle registers modules, app/build.gradle holds applicationId and deps, gradle.properties holds JVM and New Arch flags. Inspect merged AndroidManifest.xml for final permissions.
-
Filter adb logcat for RN signals: ReactNativeJS, ReactNative, and AndroidRuntime tags surface JS logs and native crashes. USB debugging needs adb reverse tcp:8081 tcp:8081 for Metro.
-
Test on API 34–35 emulators: Ancient API images hide edge-to-edge, photo picker, notification permission, and back-gesture behavior that RN 0.86 targets.
-
Wrap the app in SafeAreaProvider: Edge-to-edge Android (RN 0.86 default) draws behind system bars - inset hooks require a provider at the root layout.
-
Pair expo-status-bar with inset padding: Status bar modules style icon color; they do not pad content. Use react-native-safe-area-context for layout clearance.
-
Theme the nav bar with expo-navigation-bar: Transparent gesture nav bars need setBackgroundColorAsync and setButtonStyleAsync - often reapplied per screen on focus.
-
Assign one inset owner per axis: Do not stack SafeAreaView and manual insets.top on the same edge. Tab screens omit bottom safe edge when the tab bar handles it.
-
Request permissions in context: Explain why on a screen, then call requestPermission - never a splash-time permission dump. Play policy rejects "not core to app purpose" sensitive access.
-
Prefer the system photo picker: expo-image-picker one-off flows should avoid broad READ_MEDIA_IMAGES unless continuous gallery sync is a core feature.
-
Audit the merged manifest after prebuild: rg uses-permission android/app/src/main/AndroidManifest.xml - catch transitive permissions from unused SDKs and strip with blockedPermissions.
-
Keep Data safety aligned with SDKs: Every analytics, crash, auth, or payment SDK must appear in Play Console Data safety and your privacy policy - "no data collected" with Sentry installed is a rejection path.
-
Request POST_NOTIFICATIONS on API 33+: Before getExpoPushTokenAsync, check and request notification permission - pushes silently fail without it.
-
Enable Play App Signing on new listings: Google holds the app signing key; you keep an upload key. Upload key loss is recoverable; app signing key loss is not.
-
Let EAS manage upload keystores: Use eas credentials as team source of truth - never commit .jks files or passwords to git. Vault a downloaded backup.
-
Put Play App Signing SHA-256 in assetlinks.json: Production Android App Links verify against Google's re-signing cert - not your upload or debug fingerprint.
-
Increment versionCode every Play upload: Monotonic integers across all tracks. Use autoIncrement: true in eas.json or bump android.versionCode in app.config.ts.
-
Ship internal track before production: eas submit with "track": "internal" catches signing, Data safety, IAP, and App Links issues before public staged rollout.
-
Test Play-delivered builds for store features: EAS APK links and sideload debug builds do not fully exercise Play re-signing, billing, or verified App Links - install from internal track.
-
Validate 16 KB page size on API 35: Use 16 KB page size AVDs when available; cold-start smoke native libs before launch. Third-party .so SDKs are the usual compliance gap.
-
Maintain a device matrix beyond Pixel: Include low-RAM Samsung A-series, gesture vs 3-button nav, and a tablet or foldable width - emulator-only QA misses OEM inset and memory behavior.
-
Tune Gradle JVM and caching: Set org.gradle.jvmargs, parallel, and caching in gradle.properties - avoid habitual prebuild --clean. Enable EAS build cache for cloud compiles.
-
Profile Gradle with --scan when builds regress: Measure task time before guessing - CMake, codegen, and dex merges dominate RN 0.86; fix the actual slow task.
Stack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (expo ~57.0.4).