I integrated React Native into a native Android app and I create new instances of React Native activities from the native code.
Here is the code for the class that wraps ReactInstanceManager:
public class ReactNativeInstanceWrapper
{
private static ReactNativeInstanceWrapper instance = new ReactNativeInstanceWrapper();
public static ReactNativeInstanceWrapper getInstance() {
return instance;
}
private ReactInstanceManager reactInstanceManager;
public ReactInstanceManager GetReactInstanceManager()
{
return reactInstanceManager;
}
public ReactInstanceManager Rebuild(Application application)
{
Boolean isDebugBuild = AppBuildType.IsBuildConfigDebug(application.getBaseContext());
reactInstanceManager = null;
synchronized (this) {
reactInstanceManager = ReactInstanceManager.builder()
.setApplication(application)
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.addPackage(new ReactIntegrationPackage())
.addPackage(new PickerPackage())
.addPackage(new LinearGradientPackage())
.setUseDeveloperSupport(isDebugBuild)
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
.build();
}
if (!reactInstanceManager.hasStartedCreatingInitialContext())
{
reactInstanceManager.createReactContextInBackground();
}
return reactInstanceManager;
}
I want parse a js bundle while the app is loading and cache it, which makes loading of React Native much faster. The problem is that it looks like sharing a ReactInstanceManager between multiple activities cause some problems.
For example, in one case, if I open a Share sheet in one of my activites and than come back to my RN activity and close it, I can't open a dialog in a new RN activity. It throws a WindowManager$BadTokenException, which probably means that it tries to attach it to an activity that doesn't exist.
In a RN activity, here is how I create ReactRootView in OnCreate:
this.ReactRootView = new ReactRootView(this);
setContentView(this.ReactRootView);
ReactNativeInstanceWrapper reactNativeInstanceWrapper = ReactNativeInstanceWrapper.getInstance();
this.ReactInstanceManager = reactNativeInstanceWrapper.GetReactInstanceManager();
if (this.ReactInstanceManager == null) {
this.ReactInstanceManager = ReactNativeInstanceWrapper.getInstance().Rebuild(getApplication());
}
this.ReactRootView.startReactApplication(this.ReactInstanceManager, reactNativeComponent, initialProps);
ReactInstanceManager is supposed to set a new activity in OnResume:
@Override
protected void onResume()
{
super.onResume();
if (this.ReactInstanceManager != null) {
this.ReactInstanceManager.onHostResume(this, this);
}
}
But it looks like it still keeps references to an old activity somewhere.
So, what I ended up doing is destroying and rebuilding the instance of ReactInstanceManager every time I leave a RN activity. It's not a perfect option, but it works.
I would like to find a way to create and persist a single instance of ReactInstanceManager instead if recreating it every time in background.
Here is the solution I found.
I used MutableContextWrapper to create initial ReactRootView.
ReactRootView reactRootView = new ReactRootView(new MutableContextWrapper(originActivity));
It allowed me to substitute it with a new context in OnCreate
MutableContextWrapper contextWrapper = (MutableContextWrapper) reactRootView.getContext();
contextWrapper.setBaseContext(currentActivity);
This way I can preload the whole thing and reuse later. I found this solution by accident in one of the github issues. I wish there was a well documented way of doing that.
In older versions of React Native there also used to be an issue with old reference to a modal dialog, but it is fixed now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With