diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 3792fe60c96..9980ff8c1c6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -156,12 +156,14 @@ public class ReactInstanceManager { /* accessed from any thread */ private final JavaScriptExecutorFactory mJavaScriptExecutorFactory; + // See {@code ReactInstanceManagerBuilder} for description of all flags here. private @Nullable List mViewManagerNames = null; private final @Nullable JSBundleLoader mBundleLoader; private final @Nullable String mJSMainModulePath; /* path to JS bundle root on Metro */ private final List mPackages; private final DevSupportManager mDevSupportManager; private final boolean mUseDeveloperSupport; + private final boolean mRequireActivity; private @Nullable ComponentNameResolverManager mComponentNameResolverManager; private @Nullable RuntimeSchedulerManager mRuntimeSchedulerManager; private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener; @@ -216,6 +218,7 @@ public class ReactInstanceManager { @Nullable String jsMainModulePath, List packages, boolean useDeveloperSupport, + boolean requireActivity, @Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener, LifecycleState initialLifecycleState, @Nullable UIImplementationProvider mUIImplementationProvider, @@ -233,6 +236,7 @@ public class ReactInstanceManager { DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext); + // See {@code ReactInstanceManagerBuilder} for description of all flags here. mApplicationContext = applicationContext; mCurrentActivity = currentActivity; mDefaultBackButtonImpl = defaultHardwareBackBtnHandler; @@ -241,6 +245,7 @@ public class ReactInstanceManager { mJSMainModulePath = jsMainModulePath; mPackages = new ArrayList<>(); mUseDeveloperSupport = useDeveloperSupport; + mRequireActivity = requireActivity; Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.initDevSupportManager"); mDevSupportManager = @@ -558,16 +563,20 @@ public class ReactInstanceManager { * @param activity the activity being paused */ @ThreadConfined(UI) - public void onHostPause(Activity activity) { - Assertions.assertNotNull(mCurrentActivity); - Assertions.assertCondition( - activity == mCurrentActivity, - "Pausing an activity that is not the current activity, this is incorrect! " - + "Current activity: " - + mCurrentActivity.getClass().getSimpleName() - + " " - + "Paused activity: " - + activity.getClass().getSimpleName()); + public void onHostPause(@Nullable Activity activity) { + if (mRequireActivity) { + Assertions.assertCondition(mCurrentActivity != null); + } + if (mCurrentActivity != null) { + Assertions.assertCondition( + activity == mCurrentActivity, + "Pausing an activity that is not the current activity, this is incorrect! " + + "Current activity: " + + mCurrentActivity.getClass().getSimpleName() + + " " + + "Paused activity: " + + activity.getClass().getSimpleName()); + } onHostPause(); } @@ -583,7 +592,8 @@ public class ReactInstanceManager { * this instance of {@link ReactInstanceManager}. */ @ThreadConfined(UI) - public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) { + public void onHostResume( + @Nullable Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) { UiThreadUtil.assertOnUiThread(); mDefaultBackButtonImpl = defaultBackButtonImpl; @@ -592,7 +602,7 @@ public class ReactInstanceManager { /** Use this method when the activity resumes. */ @ThreadConfined(UI) - public void onHostResume(Activity activity) { + public void onHostResume(@Nullable Activity activity) { UiThreadUtil.assertOnUiThread(); mCurrentActivity = activity; @@ -631,8 +641,8 @@ public class ReactInstanceManager { // activity is attached to window, we can enable dev support immediately mDevSupportManager.setDevSupportEnabled(true); } - } else { - // there is no activity, we can enable dev support + } else if (!mRequireActivity) { + // there is no activity, but we can enable dev support mDevSupportManager.setDevSupportEnabled(true); } } @@ -666,7 +676,9 @@ public class ReactInstanceManager { * @param activity the activity being destroyed */ @ThreadConfined(UI) - public void onHostDestroy(Activity activity) { + public void onHostDestroy(@Nullable Activity activity) { + // In some cases, Activity may (correctly) be null. + // See mRequireActivity flag. if (activity == mCurrentActivity) { onHostDestroy(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java index c06f71b9a59..36f700e4976 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -45,6 +45,7 @@ public class ReactInstanceManagerBuilder { private @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener; private @Nullable Application mApplication; private boolean mUseDeveloperSupport; + private boolean mRequireActivity; private @Nullable LifecycleState mInitialLifecycleState; private @Nullable UIImplementationProvider mUIImplementationProvider; private @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; @@ -171,6 +172,16 @@ public class ReactInstanceManagerBuilder { return this; } + /** + * When {@code false}, indicates that correct usage of React Native will NOT involve an Activity. + * For the vast majority of Android apps in the ecosystem, this will not need to change. Unless + * you really know what you're doing, you should probably not change this! + */ + public ReactInstanceManagerBuilder setRequireActivity(boolean requireActivity) { + mRequireActivity = requireActivity; + return this; + } + /** * Sets the initial lifecycle state of the host. For example, if the host is already resumed at * creation time, we wouldn't expect an onResume call until we get an onPause call. @@ -283,6 +294,7 @@ public class ReactInstanceManagerBuilder { mJSMainModulePath, mPackages, mUseDeveloperSupport, + mRequireActivity, mBridgeIdleDebugListener, Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"), mUIImplementationProvider, diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java index d4c457ff87d..dc313d33bc0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java @@ -68,6 +68,7 @@ public abstract class ReactNativeHost { .setApplication(mApplication) .setJSMainModulePath(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) + .setRequireActivity(getShouldRequireActivity()) .setRedBoxHandler(getRedBoxHandler()) .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory()) .setUIImplementationProvider(getUIImplementationProvider()) @@ -124,6 +125,11 @@ public abstract class ReactNativeHost { return null; } + /** Returns whether or not to treat it as normal if Activity is null. */ + public boolean getShouldRequireActivity() { + return true; + } + /** * Returns the name of the main module. Determines the URL used to fetch the JS bundle from Metro. * It is only used when dev support is enabled. This is the first file to be executed once the