diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 0a82ddd54..f6db4ae35 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -25,6 +25,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.devsupport.DevSupportManager; +import com.facebook.react.devsupport.RedBoxHandler; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.uimanager.UIImplementationProvider; import com.facebook.react.uimanager.ViewManager; @@ -188,6 +189,7 @@ public abstract class ReactInstanceManager { protected @Nullable JSCConfig mJSCConfig; protected @Nullable Activity mCurrentActivity; protected @Nullable DefaultHardwareBackBtnHandler mDefaultHardwareBackBtnHandler; + protected @Nullable RedBoxHandler mRedBoxHandler; protected Builder() { } @@ -297,6 +299,11 @@ public abstract class ReactInstanceManager { return this; } + public Builder setRedBoxHandler(RedBoxHandler redBoxHandler) { + mRedBoxHandler = redBoxHandler; + return this; + } + /** * Instantiates a new {@link ReactInstanceManagerImpl}. * Before calling {@code build}, the following must be called: @@ -335,7 +342,8 @@ public abstract class ReactInstanceManager { Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"), mUIImplementationProvider, mNativeModuleCallExceptionHandler, - mJSCConfig); + mJSCConfig, + mRedBoxHandler); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java index bfaf7e6ba..b347a81bf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java @@ -57,6 +57,7 @@ import com.facebook.react.devsupport.DevServerHelper; import com.facebook.react.devsupport.DevSupportManager; import com.facebook.react.devsupport.DevSupportManagerFactory; import com.facebook.react.devsupport.ReactInstanceDevCommandsHandler; +import com.facebook.react.devsupport.RedBoxHandler; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.uimanager.AppRegistry; @@ -122,6 +123,7 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START private final MemoryPressureRouter mMemoryPressureRouter; private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; private final @Nullable JSCConfig mJSCConfig; + private @Nullable RedBoxHandler mRedBoxHandler; private final ReactInstanceDevCommandsHandler mDevInterface = new ReactInstanceDevCommandsHandler() { @@ -262,6 +264,35 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START } } + /* package */ ReactInstanceManagerImpl( + Context applicationContext, + @Nullable Activity currentActivity, + @Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler, + @Nullable String jsBundleFile, + @Nullable String jsMainModuleName, + List packages, + boolean useDeveloperSupport, + @Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener, + LifecycleState initialLifecycleState, + UIImplementationProvider uiImplementationProvider, + NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler, + @Nullable JSCConfig jscConfig) { + + this(applicationContext, + currentActivity, + defaultHardwareBackBtnHandler, + jsBundleFile, + jsMainModuleName, + packages, + useDeveloperSupport, + bridgeIdleDebugListener, + initialLifecycleState, + uiImplementationProvider, + nativeModuleCallExceptionHandler, + jscConfig, + null); + } + /* package */ ReactInstanceManagerImpl( Context applicationContext, @Nullable Activity currentActivity, @@ -274,7 +305,8 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START LifecycleState initialLifecycleState, UIImplementationProvider uiImplementationProvider, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler, - @Nullable JSCConfig jscConfig) { + @Nullable JSCConfig jscConfig, + @Nullable RedBoxHandler redBoxHandler) { initializeSoLoaderIfNecessary(applicationContext); // TODO(9577825): remove this @@ -288,11 +320,13 @@ import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START mJSMainModuleName = jsMainModuleName; mPackages = packages; mUseDeveloperSupport = useDeveloperSupport; + mRedBoxHandler = redBoxHandler; mDevSupportManager = DevSupportManagerFactory.create( applicationContext, mDevInterface, mJSMainModuleName, - useDeveloperSupport); + useDeveloperSupport, + mRedBoxHandler); mBridgeIdleDebugListener = bridgeIdleDebugListener; mLifecycleState = initialLifecycleState; mUIImplementationProvider = uiImplementationProvider; diff --git a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManager.java index 9ac350808..b3ba638c5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManager.java @@ -62,7 +62,8 @@ public abstract class XReactInstanceManager { Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"), mUIImplementationProvider, mNativeModuleCallExceptionHandler, - mJSCConfig); + mJSCConfig, + mRedBoxHandler); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java index 9f5ed6c19..cdc96efed 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/XReactInstanceManagerImpl.java @@ -67,6 +67,7 @@ import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewManager; import com.facebook.soloader.SoLoader; import com.facebook.systrace.Systrace; +import com.facebook.react.devsupport.RedBoxHandler; import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_JS_MODULE_CONFIG_END; import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_JS_MODULE_CONFIG_START; @@ -125,6 +126,7 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; private final MemoryPressureRouter mMemoryPressureRouter; private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; private final @Nullable JSCConfig mJSCConfig; + private @Nullable RedBoxHandler mRedBoxHandler; private final ReactInstanceDevCommandsHandler mDevInterface = new ReactInstanceDevCommandsHandler() { @@ -276,6 +278,37 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; UIImplementationProvider uiImplementationProvider, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler, @Nullable JSCConfig jscConfig) { + + this(applicationContext, + currentActivity, + defaultHardwareBackBtnHandler, + jsBundleFile, + jsMainModuleName, + packages, + useDeveloperSupport, + bridgeIdleDebugListener, + initialLifecycleState, + uiImplementationProvider, + nativeModuleCallExceptionHandler, + jscConfig, + null); + } + + /* package */ XReactInstanceManagerImpl( + Context applicationContext, + @Nullable Activity currentActivity, + @Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler, + @Nullable String jsBundleFile, + @Nullable String jsMainModuleName, + List packages, + boolean useDeveloperSupport, + @Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener, + LifecycleState initialLifecycleState, + UIImplementationProvider uiImplementationProvider, + NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler, + @Nullable JSCConfig jscConfig, + @Nullable RedBoxHandler redBoxHandler) { + initializeSoLoaderIfNecessary(applicationContext); // TODO(9577825): remove this @@ -289,11 +322,13 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; mJSMainModuleName = jsMainModuleName; mPackages = packages; mUseDeveloperSupport = useDeveloperSupport; + mRedBoxHandler = redBoxHandler; mDevSupportManager = DevSupportManagerFactory.create( applicationContext, mDevInterface, mJSMainModuleName, - useDeveloperSupport); + useDeveloperSupport, + mRedBoxHandler); mBridgeIdleDebugListener = bridgeIdleDebugListener; mLifecycleState = initialLifecycleState; mUIImplementationProvider = uiImplementationProvider; diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java index 22d925672..d41db6f5c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + package com.facebook.react.devsupport; import javax.annotation.Nullable; @@ -5,10 +14,6 @@ import javax.annotation.Nullable; import java.lang.reflect.Constructor; import android.content.Context; -import android.util.Log; - -import com.facebook.react.common.ReactConstants; -import com.facebook.react.common.build.ReactBuildConfig; /** * A simple factory that creates instances of {@link DevSupportManager} implementations. Uses @@ -26,6 +31,21 @@ public class DevSupportManagerFactory { ReactInstanceDevCommandsHandler reactInstanceCommandsHandler, @Nullable String packagerPathForJSBundleName, boolean enableOnCreate) { + + return create( + applicationContext, + reactInstanceCommandsHandler, + packagerPathForJSBundleName, + enableOnCreate, + null); + } + + public static DevSupportManager create( + Context applicationContext, + ReactInstanceDevCommandsHandler reactInstanceCommandsHandler, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate, + @Nullable RedBoxHandler redBoxHandler) { if (!enableOnCreate) { return new DisabledDevSupportManager(); } @@ -34,28 +54,30 @@ public class DevSupportManagerFactory { // Class.forName() with a static string. So instead we generate a quasi-dynamic string to // confuse it. String className = - new StringBuilder(DEVSUPPORT_IMPL_PACKAGE) - .append(".") - .append(DEVSUPPORT_IMPL_CLASS) - .toString(); + new StringBuilder(DEVSUPPORT_IMPL_PACKAGE) + .append(".") + .append(DEVSUPPORT_IMPL_CLASS) + .toString(); Class devSupportManagerClass = - Class.forName(className); + Class.forName(className); Constructor constructor = - devSupportManagerClass.getConstructor( - Context.class, - ReactInstanceDevCommandsHandler.class, - String.class, - boolean.class); + devSupportManagerClass.getConstructor( + Context.class, + ReactInstanceDevCommandsHandler.class, + String.class, + boolean.class, + RedBoxHandler.class); return (DevSupportManager) constructor.newInstance( - applicationContext, - reactInstanceCommandsHandler, - packagerPathForJSBundleName, - true); + applicationContext, + reactInstanceCommandsHandler, + packagerPathForJSBundleName, + true, + redBoxHandler); } catch (Exception e) { throw new RuntimeException( - "Requested enabled DevSupportManager, but DevSupportManagerImpl class was not found" + - " or could not be created", - e); + "Requested enabled DevSupportManager, but DevSupportManagerImpl class was not found" + + " or could not be created", + e); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java index ee4a7f3a6..35d7471fe 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerImpl.java @@ -111,12 +111,13 @@ public class DevSupportManagerImpl implements DevSupportManager { private boolean mIsDevSupportEnabled = false; private boolean mIsCurrentlyProfiling = false; private int mProfileIndex = 0; + private @Nullable RedBoxHandler mRedBoxHandler; public DevSupportManagerImpl( - Context applicationContext, - ReactInstanceDevCommandsHandler reactInstanceCommandsHandler, - @Nullable String packagerPathForJSBundleName, - boolean enableOnCreate) { + Context applicationContext, + ReactInstanceDevCommandsHandler reactInstanceCommandsHandler, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate) { mReactInstanceCommandsHandler = reactInstanceCommandsHandler; mApplicationContext = applicationContext; mJSAppBundleName = packagerPathForJSBundleName; @@ -160,6 +161,21 @@ public class DevSupportManagerImpl implements DevSupportManager { setDevSupportEnabled(enableOnCreate); } + public DevSupportManagerImpl( + Context applicationContext, + ReactInstanceDevCommandsHandler reactInstanceCommandsHandler, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate, + @Nullable RedBoxHandler redBoxHandler) { + + this(applicationContext, + reactInstanceCommandsHandler, + packagerPathForJSBundleName, + enableOnCreate); + + mRedBoxHandler = redBoxHandler; + } + @Override public void handleException(Exception e) { if (mIsDevSupportEnabled) { @@ -209,9 +225,12 @@ public class DevSupportManagerImpl implements DevSupportManager { errorCookie != mRedBoxDialog.getErrorCookie()) { return; } - mRedBoxDialog.setExceptionDetails( - message, - StackTraceHelper.convertJsStackTrace(details)); + StackFrame[] stack = StackTraceHelper.convertJsStackTrace(details); + mRedBoxDialog.setExceptionDetails(message, stack); + mRedBoxDialog.setErrorCookie(errorCookie); + if (mRedBoxHandler != null) { + mRedBoxHandler.handleRedbox(message, stack); + } mRedBoxDialog.show(); } }); @@ -244,6 +263,9 @@ public class DevSupportManagerImpl implements DevSupportManager { } mRedBoxDialog.setExceptionDetails(message, stack); mRedBoxDialog.setErrorCookie(errorCookie); + if (mRedBoxHandler != null) { + mRedBoxHandler.handleRedbox(message, stack); + } mRedBoxDialog.show(); } }); diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java new file mode 100644 index 000000000..61b9bce90 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxHandler.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.devsupport; + +import com.facebook.react.devsupport.StackTraceHelper.StackFrame; + +/** + * Interface used by {@link DevSupportManagerImpl} to allow interception on any redboxes + * during development and handling the information from the redbox. + * The implementation should be passed by {@link #setRedBoxHandler} in {@link ReactInstanceManager}. + */ +public interface RedBoxHandler { + void handleRedbox(String title, StackFrame[] stack); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java index 179c0c35d..aaec57919 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/StackTraceHelper.java @@ -17,7 +17,7 @@ import com.facebook.react.bridge.ReadableMap; /** * Helper class converting JS and Java stack traces into arrays of {@link StackFrame} objects. */ -/* package */ class StackTraceHelper { +public class StackTraceHelper { /** * Represents a generic entry in a stack trace, be it originally from JS or Java.