react-native-reanimated: Incompatibility with Custom React Native Integration with Existing Android Apps (Crash on launch)

Description

The last v2.3.0 version introduced an issue that affects Android apps that have a custom React Native integration (see integration-with-existing-apps)

Currently, there is no documentation about how to integrate react-native-reanimated for those kinds of apps. I managed to make it work doing what I show in the code example section, on the main React Native activity.

I was not having any kind of issue with this integration using v2.2.4. After I updated to v2.3.0 I started facing this issue. (See crash on launch capture below)

I also checked where the issue comes from, and as the capture shows, it seems like that it was introduced here. https://github.com/software-mansion/react-native-reanimated/blob/main/android/src/main/java/com/swmansion/reanimated/ReanimatedPackage.java#L70

It can be seen there that is trying to cast to a ReactApplication when instead the ReactInstanceManager builder in this case handles the application context just as Application https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java#L155

Expected behavior

Android app should run correctly.

Actual behavior & steps to reproduce

Android app is crashing on launch. image

Snack or minimal code example

import com.swmansion.reanimated.ReanimatedJSIModulePackage
....
public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication()) // <= Crashing because of this
                .setCurrentActivity(this)
                .setBundleAssetName("index.android.bundle")
                .setJSIModulesPackage(new ReanimatedJSIModulePackage()) // <- Adding reanimated here
                .addPackages(packages)
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
       ...
    }
    ...
}

Example for reproducing this issue

https://github.com/WFolini/custom-rn-with-reanimated

Package versions

  • React Native: 64.2
  • React Native Reanimated: 2.3.0
  • NodeJS: 16.8.0
  • Java & Gradle: 1.8.0_292 & 6.8.3

Affected platforms

  • Android
  • iOS
  • Web

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 4
  • Comments: 25 (4 by maintainers)

Commits related to this issue

Most upvoted comments

In https://github.com/software-mansion/react-native-reanimated/pull/2863 I explain the way that you can make it work by applying a different mechanism to infer the ReactInstanceManager instance needed for react-native-reanimated. Have you guys tried it?

In this example code below, on getReactInstanceManager method, you guys have to implement by yourself the MainActivity.getReactInstanceManager(), or simply just return mReactInstanceManager

public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {

    private static ReactInstanceManager mReactInstanceManager;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       ...
        List<ReactPackage> packages = new PackageList(getApplication()).getPackages();

        // Adding manually Reanimated package here, with overriding getReactInstanceManager method
         packages.add(new ReanimatedPackage() {
            @Override
            public ReactInstanceManager getReactInstanceManager(ReactApplicationContext reactContext) {
               // Implement here your way to get the ReactInstanceManager
               return MainActivity.getReactInstanceManager();
            }
         });

        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setCurrentActivity(this)
                .setBundleAssetName("index.android.bundle")
                .setJSMainModulePath("index")
                .setJSIModulesPackage(new ReanimatedJSIModulePackage()) // Adding ReanimatedJSIModulePackage here
                .addPackages(packages)
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
       
        ...
    }
...
}

Rn can be used to build libraries too. It might not be desirable / possible for certain apps to implement ReactApplication.

@WFolini Thanks for this. Do you know what should replace the .setJSIModulesPackage(new ReanimatedJSIModulePackage()) part in React Native Reanimated 3.x with Fabric, now that ReanimatedJSIModulePackage has been removed?

I’m trying to adapt this to work in a React Fragment in a custom integration with new architecture enabled, and it gives this error, which I think is another new instance of this underlying issue (Reanimated doing some brittle under the hood auto-apply magic that assumes the default React Native android native app is being used):

com.facebook.react.uimanager.ReanimatedUIManager cannot be cast to com.facebook.react.fabric.FabricUIManager

This doesn’t have anything to do with Proguard. It’s an issue that exists whenever the android app uses ReactInstanceManager to build the React Native activity.

Adding -keep to Proguard didn’t fixed it for me. I downgraded to 2.2.4.

@wfolini v3 module ReanimatedJSIModulePackage has been deprecated

What worked for me here was, copying from the Reanimated / Fabric example repo - in particular, its MainActivity) which seems to include some changes since React Native 0.71 that are expected/essential but not mentioned in the out-of-date React Native app integration documentation?

The crucial one for me was adding this (this is my Kotlin adaptation of the linked example’s Java plus a few tweaks to make it fit):

    protected open fun createReactActivityDelegate(): ReactActivityDelegate? {
        return DefaultReactActivityDelegate(
            this as ReactActivity, // My activity class more complex than `extends ReactActivity` so needed `as`
            "my-main-component-name",  // Taken from the "name" field in `app.json` in react native app root dir
            DefaultNewArchitectureEntryPoint.fabricEnabled,
            DefaultNewArchitectureEntryPoint.concurrentReactEnabled
        )
    }

This makes builds work, but I’m then getting errors caused by components missing stateNode so I suspect there’s something else essential too.