facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.2k stars 24.33k forks source link

Brownfield app crashes when using ReactFragment on new architecture #46566

Closed gmantuanrosa closed 1 month ago

gmantuanrosa commented 1 month ago

Description

I have an Android app that is using React Native for some activities and I decided to follow React Native documentation to add into Fragments as well. My App has a mix of XML and Compose navigation and Fragments for the Bottom Tab Navigation -- one of the tab being React Native.

It does work fine until I enable the new architecture, generating app crashes once the Fragment is mounted. I also had the same issue creating a custom Activity with the same crash behavior.

Steps to reproduce

  1. Create a new Android Project;
  2. Integrate React Native following the documentation;
  3. Create an Android Fragment;
  4. Integrate React Fragment into Android Fragment;
  5. Init the App and launch the Fragment [ FAILED ]

React Native Version

0.75.3

Affected Platforms

Runtime - Android

Output of npx react-native info

command not found even running `npx @react-native-community/cli info`

Stacktrace or Logs

E  FATAL EXCEPTION: main
          Process: com.example.composereactnativeapp, PID: 15959
          java.lang.NullPointerException: Attempt to invoke interface method 'com.facebook.react.interfaces.fabric.ReactSurface com.facebook.react.ReactHost.createSurface(android.content.Context, java.lang.String, android.os.Bundle)' on a null object reference
           at com.facebook.react.ReactDelegate.loadApp(ReactDelegate.java:283)
           at com.facebook.react.ReactDelegate.loadApp(ReactDelegate.java:275)
           at com.facebook.react.ReactFragment.onCreateView(ReactFragment.java:95)
           at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3119)
           at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:577)
           at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:286)
           at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2164)
           at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2059)
           at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
           at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3277)
           at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:3180)
           at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3153)
           at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:608)
           at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:286)
           at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2164)
           at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2059)
           at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
           at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:702)
           at android.os.Handler.handleCallback(Handler.java:938)
           at android.os.Handler.dispatchMessage(Handler.java:99)
           at android.os.Looper.loopOnce(Looper.java:201)
           at android.os.Looper.loop(Looper.java:288)
           at android.app.ActivityThread.main(ActivityThread.java:7870)
           at java.lang.reflect.Method.invoke(Native Method)
           at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

Reproducer

https://github.com/gmantuanrosa/brownfield-rn-fragment

Screenshots and Videos

No response

Sky commented 1 month ago

Have the same issue when just trying to enable New Architecture on existing project. RN 0.75.3

FATAL EXCEPTION: main
        Process: com.***, PID: 12871
        java.lang.RuntimeException: Unable to start activity ComponentInfo{com.***/com.***.AppActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'com.facebook.react.interfaces.fabric.ReactSurface com.facebook.react.ReactHost.createSurface(android.content.Context, java.lang.String, android.os.Bundle)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
        Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'com.facebook.react.interfaces.fabric.ReactSurface com.facebook.react.ReactHost.createSurface(android.content.Context, java.lang.String, android.os.Bundle)' on a null object reference
        at com.facebook.react.ReactDelegate.loadApp(ReactDelegate.java:283)
        at com.facebook.react.ReactActivityDelegate.loadApp(ReactActivityDelegate.java:137)
        at com.facebook.react.ReactActivityDelegate.onCreate(ReactActivityDelegate.java:132)
        at com.facebook.react.ReactActivity.onCreate(ReactActivity.java:47)
        at com.***.AppActivity.onCreate(AppActivity.java:30)
        at android.app.Activity.performCreate(Activity.java:7994)
        at android.app.Activity.performCreate(Activity.java:7978)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7656) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
cortinico commented 1 month ago

Thanks for attaching a reproducer @gmantuanrosa

There is a bug inside ReactFragment that I need to fix in order for this to work properly. I'll get back to you

gmantuanrosa commented 1 month ago

Thanks for attaching a reproducer @gmantuanrosa

There is a bug inside ReactFragment that I need to fix in order for this to work properly. I'll get back to you

Thanks to look into it @cortinico!

I had the same issue when using Activity as well (also mentioned by @Sky). Probably because they share the same ReactDelegate?

Can you say that this will also fix Activity issues? If you want I can update the repro to have a React Activity being launched as well 😄

cortinico commented 1 month ago

Can you say that this will also fix Activity issues? If you want I can update the repro to have a React Activity being launched as well 😄

Yes please. I'd say we first need to fix the Activity scenario, and then get back to the Fragment one. If you could update the repo to use activities instead, it would be extremely helpful

gmantuanrosa commented 1 month ago

Can you say that this will also fix Activity issues? If you want I can update the repro to have a React Activity being launched as well 😄

Yes please. I'd say we first need to fix the Activity scenario, and then get back to the Fragment one. If you could update the repo to use activities instead, it would be extremely helpful

Fortunately I could make it work the Fabric React Activity but there is now an example in the reproducible, I still can't make Fragments work.

Also I only got this activity issue once and I don't know how can I reproduce it again.

cortinico commented 1 month ago

Ok so sharing my findings so far (perhaps they'll help you @gmantuanrosa) but I need to get back to this next week.

  1. ReactFragment as it is at the moment is not properly creating the ReactDelegate. When on Bridgeless, you need to have a valid ReactHost instance as otherwise the app will crash as you're seeing.

  2. The patch to circumvent this is to do the following:

  3. Enable build from source in your project https://reactnative.dev/contributing/how-to-build-from-source

  4. Downgrade AGP to 8.5.0 (this is needed only because of the build from source).

  5. Edit the ReactFragment.java here as follows:

https://github.com/facebook/react-native/blob/893a4b30b1596fff1a85e66139ec697674673034/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactFragment.java#L59-L75

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    String mainComponentName = null;
    Bundle launchOptions = null;
    Boolean fabricEnabled = null;
    if (getArguments() != null) {
      mainComponentName = getArguments().getString(ARG_COMPONENT_NAME);
      launchOptions = getArguments().getBundle(ARG_LAUNCH_OPTIONS);
      fabricEnabled = getArguments().getBoolean(ARG_FABRIC_ENABLED);
    }
    if (mainComponentName == null) {
      throw new IllegalStateException("Cannot loadApp if component name is null");
    }
    if (!ReactFeatureFlags.enableBridgelessArchitecture) {
      mReactDelegate =
              new ReactDelegate(
                      getActivity(), getReactNativeHost(), mainComponentName, launchOptions, fabricEnabled);
    } else {
      mReactDelegate =
              new ReactDelegate(
                      getActivity(), getReactHost(), mainComponentName, launchOptions);
    }
  }

  private ReactHost getReactHost() {
    return ((ReactApplication) getActivity().getApplication()).getReactHost();
  }

You will then observe that the app crashes with a different crash:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first

Full stacktrace:

09-20 17:28:48.351 18853 18853 E AndroidRuntime: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.view.ViewGroup.addViewInner(ViewGroup.java:5247)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.view.ViewGroup.addView(ViewGroup.java:5076)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentContainerView.addView(FragmentContainerView.kt:269)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.view.ViewGroup.addView(ViewGroup.java:5016)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentStateManager.addViewToContainer(FragmentStateManager.java:901)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:585)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:286)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2164)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2059)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:702)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.os.Handler.handleCallback(Handler.java:938)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:99)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.os.Looper.loopOnce(Looper.java:201)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:288)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at android.app.ActivityThread.main(ActivityThread.java:7842)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at java.lang.reflect.Method.invoke(Native Method)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
09-20 17:28:48.351 18853 18853 E AndroidRuntime:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
09-20 17:28:48.352 12150 14336 W ActivityTaskManager:   Force finishing activity com.example.composereactnativeapp/.activity.MainActivity

I haven't been able to isolate why is it crashing with this error. It can either be because of the NavHost from Jetpack Compose or it can be a bug inside React Native.

Any investigation is more than appreciated @gmantuanrosa

cortinico commented 1 month ago

I've spent more time on this and finally fixed it.

  1. The docs are udpated here: https://github.com/facebook/react-native-website/pull/4232
  2. Those two PR will fix your issue:
    1. https://github.com/facebook/react-native/pull/46623
    2. https://github.com/facebook/react-native/pull/46623

I've opened a Pick request for those fixes to be included inside 0.76 here: