Skip to content

fix(Android): make fragment restoration R8-safe - #4539

Closed
ngocdevv wants to merge 1 commit into
software-mansion:mainfrom
ngocdevv:codex/fix-r8-fragment-restoration
Closed

fix(Android): make fragment restoration R8-safe#4539
ngocdevv wants to merge 1 commit into
software-mansion:mainfrom
ngocdevv:codex/fix-r8-fragment-restoration

Conversation

@ngocdevv

@ngocdevv ngocdevv commented Aug 22, 2026

Copy link
Copy Markdown

Description

Closes #4505.

RNScreensFragmentFactory currently identifies screen fragments by checking whether the restored class name starts with the library package. R8 can repackage those classes when optimized resource shrinking is enabled, so the check falls through to normal fragment restoration and the screen fragment constructor throws.

This change makes restoration independent of class and package names.

Changes

  • Added an internal RNScreensFragment marker for fragments owned by the library.
  • Marked the legacy screen/modal fragments and the Stack v5 / Tabs fragments.
  • Updated RNScreensFragmentFactory to load the fragment class and check the marker with isAssignableFrom before substituting AutoRemovingFragment.

Test plan

Used the maintainer-confirmed reproducer from https://github.com/t0maboro/RNS4505 with React Native 0.86.2, android.r8.optimizedResourceShrinking=true, minification, and resource shrinking enabled.

Control (react-native-screens 4.25.0):

  1. Built and installed the release APK on an Android 36 emulator.
  2. Confirmed R8 moved ScreenFragment and ScreenStackFragment to v3.D and v3.N.
  3. Pushed Home -> Second -> Third, backgrounded the app, killed its process, and relaunched it.
  4. Relaunch crashed with Unable to instantiate fragment v3.N and Screen fragments should never be restored.

Patched build from this branch:

  1. Packed the branch as an npm tarball and rebuilt the same release reproducer.
  2. Confirmed R8 still repackaged all tested fragment classes outside com.swmansion.rnscreens.
  3. Repeated the same navigation, background, process-kill, and cold-start sequence.
  4. The app relaunched normally and the crash log remained empty.

Additional checks:

  • yarn lint-android
  • yarn check-types
  • FabricExample/android/gradlew :app:assembleDebug --console=plain -PreactNativeArchitectures=arm64-v8a
  • Reproducer android/gradlew :app:assembleRelease --console=plain -PreactNativeArchitectures=arm64-v8a

Checklist

  • Included a code example that can be used to test this change (linked reproducer).
  • No visual changes.
  • No public API changes.
  • Local CI-equivalent lint, type-check, debug build, and R8 release build pass.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Android screen restoration to reliably recognize library-managed screens, including when class names are obfuscated.
    • Preserved fallback handling for other fragment types.
    • Added consistent identification across standard, modal, stack, and tab screens.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1843d7e5-bb0c-437b-b3c4-3a562c22ead9

📥 Commits

Reviewing files that changed from the base of the PR and between 8fe8b87 and 1ce5fd7.

📒 Files selected for processing (6)
  • android/src/main/java/com/swmansion/rnscreens/fragment/restoration/RNScreensFragment.kt
  • android/src/main/java/com/swmansion/rnscreens/fragment/restoration/RNScreensFragmentFactory.kt
  • android/src/main/java/com/swmansion/rnscreens/legacy/ScreenFragment.kt
  • android/src/main/java/com/swmansion/rnscreens/legacy/ScreenModalFragment.kt
  • android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt
  • android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenFragment.kt

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The Android restoration logic adds an RNScreensFragment marker interface to screen fragments. RNScreensFragmentFactory now uses class assignability instead of package-name matching to identify these fragments.

Changes

Fragment restoration identification

Layer / File(s) Summary
Marker contract and fragment implementations
android/src/main/java/com/swmansion/rnscreens/fragment/restoration/RNScreensFragment.kt, android/src/main/java/com/swmansion/rnscreens/legacy/*, android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt, android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenFragment.kt
The RNScreensFragment marker interface is added. Screen fragment implementations now implement the interface.
Factory fragment identification
android/src/main/java/com/swmansion/rnscreens/fragment/restoration/RNScreensFragmentFactory.kt
The factory loads the fragment class and checks assignability to RNScreensFragment. Matching classes use AutoRemovingFragment; other classes use the superclass implementation.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 1ce5f

The PR makes Android fragment restoration resilient to R8 class repackaging, with no actionable merge-blocking risk remaining after normal checks and review.

Suggested reviewers: kmichalikk

Sequence Diagram(s)

sequenceDiagram
  participant RNScreensFragmentFactory
  participant RNScreensFragment
  participant AutoRemovingFragment
  participant FragmentFactory
  RNScreensFragmentFactory->>RNScreensFragmentFactory: Load className
  RNScreensFragmentFactory->>RNScreensFragment: Check assignability
  alt Class implements RNScreensFragment
    RNScreensFragmentFactory->>AutoRemovingFragment: Instantiate replacement fragment
  else Class does not implement RNScreensFragment
    RNScreensFragmentFactory->>FragmentFactory: Delegate instantiate
  end
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the Android fragment restoration fix and its R8 safety goal.
Linked Issues check ✅ Passed The changes replace package-name detection with marker-interface detection and cover the affected React Native Screens fragments, addressing issue #4505.
Out of Scope Changes check ✅ Passed All changes support R8-safe Android fragment restoration and remain within the linked issue scope.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ngocdevv

Copy link
Copy Markdown
Author

Superseded by #4540, which uses the contributor branch fix/r8-fragment-restoration without the codex/ prefix.

@ngocdevv ngocdevv closed this Aug 22, 2026
@ngocdevv
ngocdevv deleted the codex/fix-r8-fragment-restoration branch August 22, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant