crash gracefully on fatal js errors (take two)

Reviewed By: AaaChiuuu

Differential Revision: D2773513

fb-gh-sync-id: f8e50ad12e808caf1d85c6c7859c04fcabdaaeae
This commit is contained in:
Felix Oghina 2016-01-04 13:16:58 -08:00 committed by facebook-github-bot-7
parent f2bdb79782
commit 4866ec2767
3 changed files with 33 additions and 14 deletions

View File

@ -19,6 +19,7 @@ import android.app.Application;
import android.content.Intent;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
@ -159,6 +160,7 @@ public abstract class ReactInstanceManager {
protected boolean mUseDeveloperSupport;
protected @Nullable LifecycleState mInitialLifecycleState;
protected @Nullable UIImplementationProvider mUIImplementationProvider;
protected @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
protected Builder() {
}
@ -242,6 +244,16 @@ public abstract class ReactInstanceManager {
return this;
}
/**
* Set the exception handler for all native module calls. If not set, the default
* {@link DevSupportManager} will be used, which shows a redbox in dev mode and rethrows
* (crashes the app) in prod mode.
*/
public Builder setNativeModuleCallExceptionHandler(NativeModuleCallExceptionHandler handler) {
mNativeModuleCallExceptionHandler = handler;
return this;
}
/**
* Instantiates a new {@link ReactInstanceManagerImpl}.
* Before calling {@code build}, the following must be called:
@ -274,7 +286,8 @@ public abstract class ReactInstanceManager {
mUseDeveloperSupport,
mBridgeIdleDebugListener,
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
mUIImplementationProvider);
mUIImplementationProvider,
mNativeModuleCallExceptionHandler);
}
}
}

View File

@ -36,6 +36,7 @@ import com.facebook.react.bridge.JavaScriptExecutor;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.JavaScriptModulesConfig;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.NativeModuleRegistry;
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
import com.facebook.react.bridge.ProxyJavaScriptExecutor;
@ -103,6 +104,7 @@ import com.facebook.systrace.Systrace;
private volatile boolean mHasStartedCreatingInitialContext = false;
private final UIImplementationProvider mUIImplementationProvider;
private final MemoryPressureRouter mMemoryPressureRouter;
private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
private final ReactInstanceDevCommandsHandler mDevInterface =
new ReactInstanceDevCommandsHandler() {
@ -198,7 +200,8 @@ import com.facebook.systrace.Systrace;
boolean useDeveloperSupport,
@Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
LifecycleState initialLifecycleState,
UIImplementationProvider uiImplementationProvider) {
UIImplementationProvider uiImplementationProvider,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
initializeSoLoaderIfNecessary(applicationContext);
// TODO(9577825): remove this
@ -222,6 +225,7 @@ import com.facebook.systrace.Systrace;
mLifecycleState = initialLifecycleState;
mUIImplementationProvider = uiImplementationProvider;
mMemoryPressureRouter = new MemoryPressureRouter(applicationContext);
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
}
@Override
@ -660,13 +664,16 @@ import com.facebook.systrace.Systrace;
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(mDevSupportManager);
.setNativeModuleCallExceptionHandler(exceptionHandler);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
CatalystInstance catalystInstance;

View File

@ -155,7 +155,7 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
public void handleException(Exception e) {
if (mIsDevSupportEnabled) {
FLog.e(ReactConstants.TAG, "Exception in native call from JS", e);
showNewError(e.getMessage(), StackTraceHelper.convertJavaStackTrace(e), JAVA_ERROR_COOKIE);
showNewJavaError(e.getMessage(), e);
} else {
if (e instanceof RuntimeException) {
// Because we are rethrowing the original exception, the original stacktrace will be
@ -167,6 +167,10 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
}
}
public void showNewJavaError(String message, Throwable e) {
showNewError(message, StackTraceHelper.convertJavaStackTrace(e), JAVA_ERROR_COOKIE);
}
/**
* Add option item to dev settings dialog displayed by this manager. In the case user select given
* option from that dialog, the appropriate handler passed as {@param optionHandler} will be
@ -555,10 +559,9 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
new Runnable() {
@Override
public void run() {
showNewError(
showNewJavaError(
mApplicationContext.getString(R.string.catalyst_remotedbg_error),
StackTraceHelper.convertJavaStackTrace(cause),
JAVA_ERROR_COOKIE);
cause);
}
});
}
@ -590,15 +593,11 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
public void run() {
if (cause instanceof DebugServerException) {
DebugServerException debugServerException = (DebugServerException) cause;
showNewError(
debugServerException.description,
StackTraceHelper.convertJavaStackTrace(cause),
JAVA_ERROR_COOKIE);
showNewJavaError(debugServerException.description, cause);
} else {
showNewError(
showNewJavaError(
mApplicationContext.getString(R.string.catalyst_jsload_error),
StackTraceHelper.convertJavaStackTrace(cause),
JAVA_ERROR_COOKIE);
cause);
}
}
});