From b4f1f8823058ab68a24032afd43b9df0f6b5d1a8 Mon Sep 17 00:00:00 2001
From: Solimando Damien
Date: Mon, 23 Jul 2018 08:37:50 +0200
Subject: [PATCH 1/6] Don't overwrite pre-existing native handlers on iOS
---
ios/ReactNativeExceptionHandler.m | 51 +++++++++++++++++++------------
1 file changed, 32 insertions(+), 19 deletions(-)
diff --git a/ios/ReactNativeExceptionHandler.m b/ios/ReactNativeExceptionHandler.m
index e8bae91..7cb0a71 100644
--- a/ios/ReactNativeExceptionHandler.m
+++ b/ios/ReactNativeExceptionHandler.m
@@ -28,24 +28,29 @@ - (dispatch_queue_t)methodQueue
//variable to hold the custom error handler passed while customizing native handler
void (^nativeErrorCallbackBlock)(NSException *exception, NSString *readeableException);
+// variable to hold the previously defined error handler
+NSUncaughtExceptionHandler* previousNativeErrorCallbackBlock;
+
+BOOL callPreviousNativeErrorCallbackBlock = false;
+
//variable to hold the js error handler when setting up the error handler in RN.
void (^jsErrorCallbackBlock)(NSException *exception, NSString *readeableException);
//variable that holds the default native error handler
void (^defaultNativeErrorCallbackBlock)(NSException *exception, NSString *readeableException) =
^(NSException *exception, NSString *readeableException){
-
+
UIAlertController* alert = [UIAlertController
alertControllerWithTitle:@"Unexpected error occured"
message:[NSString stringWithFormat:@"%@\n%@",
@"Apologies..The app will close now \nPlease restart the app\n",
readeableException]
preferredStyle:UIAlertControllerStyleAlert];
-
+
UIApplication* app = [UIApplication sharedApplication];
UIViewController * rootViewController = app.delegate.window.rootViewController;
[rootViewController presentViewController:alert animated:YES completion:nil];
-
+
[NSTimer scheduledTimerWithTimeInterval:5.0
target:[ReactNativeExceptionHandler class]
selector:@selector(releaseExceptionHold)
@@ -53,7 +58,6 @@ - (dispatch_queue_t)methodQueue
repeats:NO];
};
-
// ====================================
// REACT NATIVE MODULE EXPOSED METHODS
// ====================================
@@ -61,12 +65,15 @@ - (dispatch_queue_t)methodQueue
RCT_EXPORT_MODULE();
// METHOD TO INITIALIZE THE EXCEPTION HANDLER AND SET THE JS CALLBACK BLOCK
-RCT_EXPORT_METHOD(setHandlerforNativeException:(RCTResponseSenderBlock)callback)
+RCT_EXPORT_METHOD(setHandlerforNativeException:(BOOL)callPreviouslyDefinedHandler withCallback: (RCTResponseSenderBlock)callback)
{
jsErrorCallbackBlock = ^(NSException *exception, NSString *readeableException){
callback(@[readeableException]);
};
-
+
+ previousNativeErrorCallbackBlock = NSGetUncaughtExceptionHandler();
+ callPreviousNativeErrorCallbackBlock = callPreviouslyDefinedHandler;
+
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
@@ -105,14 +112,19 @@ - (void)handleException:(NSException *)exception
[exception reason],
[[exception userInfo] objectForKey:RNUncaughtExceptionHandlerAddressesKey]];
dismissApp = false;
-
+
+
+ if (callPreviousNativeErrorCallbackBlock && previousNativeErrorCallbackBlock) {
+ previousNativeErrorCallbackBlock(exception);
+ }
+
if(nativeErrorCallbackBlock != nil){
nativeErrorCallbackBlock(exception,readeableError);
}else{
defaultNativeErrorCallbackBlock(exception,readeableError);
}
jsErrorCallbackBlock(exception,readeableError);
-
+
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (!dismissApp)
@@ -127,9 +139,9 @@ - (void)handleException:(NSException *)exception
i++;
}
}
-
+
CFRelease(allModes);
-
+
NSSetUncaughtExceptionHandler(NULL);
signal(SIGABRT, SIG_DFL);
signal(SIGILL, SIG_DFL);
@@ -137,9 +149,9 @@ - (void)handleException:(NSException *)exception
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
-
+
kill(getpid(), [[[exception userInfo] objectForKey:RNUncaughtExceptionHandlerSignalKey] intValue]);
-
+
}
@@ -154,14 +166,14 @@ void HandleException(NSException *exception)
{
return;
}
-
+
NSArray *callStack = [ReactNativeExceptionHandler backtrace];
NSMutableDictionary *userInfo =
[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
[userInfo
setObject:callStack
forKey:RNUncaughtExceptionHandlerAddressesKey];
-
+
[[[ReactNativeExceptionHandler alloc] init]
performSelectorOnMainThread:@selector(handleException:)
withObject:
@@ -179,17 +191,17 @@ void SignalHandler(int signal)
{
return;
}
-
+
NSMutableDictionary *userInfo =
[NSMutableDictionary
dictionaryWithObject:[NSNumber numberWithInt:signal]
forKey:RNUncaughtExceptionHandlerSignalKey];
-
+
NSArray *callStack = [ReactNativeExceptionHandler backtrace];
[userInfo
setObject:callStack
forKey:RNUncaughtExceptionHandlerAddressesKey];
-
+
[[[ReactNativeExceptionHandler alloc] init]
performSelectorOnMainThread:@selector(handleException:)
withObject:
@@ -216,7 +228,7 @@ + (NSArray *)backtrace
void* callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
-
+
int i;
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
for (
@@ -228,8 +240,9 @@ + (NSArray *)backtrace
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
-
+
return backtrace;
}
@end
+
From 56914cf24f0c0f827c6f1d710acb4fdd18dc6b96 Mon Sep 17 00:00:00 2001
From: Solimando Damien
Date: Mon, 23 Jul 2018 08:55:44 +0200
Subject: [PATCH 2/6] Allow to define a custom Native handler without
displaying Activity
---
.../NativeExceptionHandlerIfc.java | 5 +
.../ReactNativeExceptionHandlerModule.java | 114 ++++++++++--------
2 files changed, 70 insertions(+), 49 deletions(-)
create mode 100644 android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java
diff --git a/android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java b/android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java
new file mode 100644
index 0000000..babb49f
--- /dev/null
+++ b/android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java
@@ -0,0 +1,5 @@
+package com.masteratul.exceptionhandler;
+
+public interface NativeExceptionHandlerIfc {
+ void handleNativeException(Thread thread, Throwable throwable);
+}
diff --git a/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java b/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java
index eba89dc..ffe47e7 100644
--- a/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java
+++ b/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java
@@ -1,67 +1,83 @@
package com.masteratul.exceptionhandler;
+
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
+import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.bridge.Callback;
public class ReactNativeExceptionHandlerModule extends ReactContextBaseJavaModule {
- private ReactApplicationContext reactContext;
+ private ReactApplicationContext reactContext;
private Activity activity;
private static Class errorIntentTargetClass = DefaultErrorScreen.class;
+ private static NativeExceptionHandlerIfc nativeExceptionHandler;
private Callback callbackHolder;
private Thread.UncaughtExceptionHandler originalHandler;
public ReactNativeExceptionHandlerModule(ReactApplicationContext reactContext) {
- super(reactContext);
- this.reactContext = reactContext;
- }
-
- @Override
- public String getName() {
- return "ReactNativeExceptionHandler";
- }
-
-
- @ReactMethod
- public void setHandlerforNativeException(final boolean forceToQuit, Callback customHandler){
- callbackHolder = customHandler;
- originalHandler = Thread.getDefaultUncaughtExceptionHandler();
-
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread thread, Throwable throwable) {
- activity = getCurrentActivity();
- String stackTraceString = Log.getStackTraceString(throwable);
- callbackHolder.invoke(stackTraceString);
- Log.d("ERROR",stackTraceString);
-
-
- Intent i = new Intent();
- i.setClass(activity, errorIntentTargetClass);
- i.putExtra("stack_trace_string",stackTraceString);
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- activity.startActivity(i);
- activity.finish();
-
- if (originalHandler != null) {
- originalHandler.uncaughtException(thread, throwable);
- }
-
- if (forceToQuit) {
- System.exit(0);
- }
- }
- });
- }
-
- public static void replaceErrorScreenActivityClass(Class errorScreenActivityClass){
- errorIntentTargetClass = errorScreenActivityClass;
- }
-}
+ super(reactContext);
+ this.reactContext = reactContext;
+ }
+
+ @Override
+ public String getName() {
+ return "ReactNativeExceptionHandler";
+ }
+
+
+ @ReactMethod
+ public void setHandlerforNativeException(
+ final boolean executeOriginalUncaughtExceptionHandler,
+ final boolean forceToQuit,
+ Callback customHandler) {
+
+ callbackHolder = customHandler;
+ originalHandler = Thread.getDefaultUncaughtExceptionHandler();
+
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
+
+ String stackTraceString = Log.getStackTraceString(throwable);
+ callbackHolder.invoke(stackTraceString);
+
+ if (executeOriginalUncaughtExceptionHandler && originalHandler != null) {
+ originalHandler.uncaughtException(thread, throwable);
+ }
+
+ if (nativeExceptionHandler != null) {
+ nativeExceptionHandler.handleNativeException(thread, throwable);
+ } else {
+ activity = getCurrentActivity();
+
+ Intent i = new Intent();
+ i.setClass(activity, errorIntentTargetClass);
+ i.putExtra("stack_trace_string",stackTraceString);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ activity.startActivity(i);
+ activity.finish();
+
+ if (forceToQuit) {
+ System.exit(0);
+ }
+
+ }
+ }
+ });
+ }
+
+ public static void replaceErrorScreenActivityClass(Class errorScreenActivityClass){
+ errorIntentTargetClass = errorScreenActivityClass;
+ }
+
+ public static void setNativeExceptionHandler(NativeExceptionHandlerIfc nativeExceptionHandler) {
+ ReactNativeExceptionHandlerModule.nativeExceptionHandler = nativeExceptionHandler;
+ }
+}
\ No newline at end of file
From 86dcd5a8b26574586da3cf01231b80a5aef1e2c8 Mon Sep 17 00:00:00 2001
From: Solimando Damien
Date: Mon, 23 Jul 2018 08:56:09 +0200
Subject: [PATCH 3/6] make the execution of the previously defined handler
optional
---
index.js | 61 +++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 45 insertions(+), 16 deletions(-)
diff --git a/index.js b/index.js
index e917cc0..1eeb236 100644
--- a/index.js
+++ b/index.js
@@ -1,14 +1,23 @@
+import { NativeModules, Platform } from "react-native";
-import {NativeModules, Platform} from 'react-native';
+const { ReactNativeExceptionHandler } = NativeModules;
-const {ReactNativeExceptionHandler} = NativeModules;
+const noop = () => { };
-const noop = () => {};
-
-export const setJSExceptionHandler = (customHandler = noop, allowedInDevMode = false) => {
- if ((typeof allowedInDevMode !== 'boolean') || (typeof customHandler !== 'function')) {
- console.log('setJSExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean');
- console.log('Not setting the JS handler .. please fix setJSExceptionHandler call');
+export const setJSExceptionHandler = (
+ customHandler = noop,
+ allowedInDevMode = false
+) => {
+ if (
+ typeof allowedInDevMode !== "boolean" ||
+ typeof customHandler !== "function"
+ ) {
+ console.log(
+ "setJSExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean"
+ );
+ console.log(
+ "Not setting the JS handler .. please fix setJSExceptionHandler call"
+ );
return;
}
const allowed = allowedInDevMode ? true : !__DEV__;
@@ -16,22 +25,42 @@ export const setJSExceptionHandler = (customHandler = noop, allowedInDevMode = f
global.ErrorUtils.setGlobalHandler(customHandler);
console.error = (message, error) => global.ErrorUtils.reportError(error); // sending console.error so that it can be caught
} else {
- console.log('Skipping setJSExceptionHandler: Reason: In DEV mode and allowedInDevMode = false');
+ console.log(
+ "Skipping setJSExceptionHandler: Reason: In DEV mode and allowedInDevMode = false"
+ );
}
};
export const getJSExceptionHandler = () => global.ErrorUtils.getGlobalHandler();
-export const setNativeExceptionHandler = (customErrorHandler = noop, forceApplicationToQuit = true) => {
- if ((typeof customErrorHandler !== 'function') || (typeof forceApplicationToQuit !== 'boolean')) {
- console.log('setNativeExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean');
- console.log('Not setting the native handler .. please fix setNativeExceptionHandler call');
+export const setNativeExceptionHandler = (
+ customErrorHandler = noop,
+ forceApplicationToQuit = true,
+ executeDefaultHandler = false
+) => {
+ if (
+ typeof customErrorHandler !== "function" ||
+ typeof forceApplicationToQuit !== "boolean"
+ ) {
+ console.log(
+ "setNativeExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean"
+ );
+ console.log(
+ "Not setting the native handler .. please fix setNativeExceptionHandler call"
+ );
return;
}
- if (Platform.OS === 'ios') {
- ReactNativeExceptionHandler.setHandlerforNativeException(customErrorHandler);
+ if (Platform.OS === "ios") {
+ ReactNativeExceptionHandler.setHandlerforNativeException(
+ executeDefaultHandler,
+ customErrorHandler
+ );
} else {
- ReactNativeExceptionHandler.setHandlerforNativeException(forceApplicationToQuit, customErrorHandler);
+ ReactNativeExceptionHandler.setHandlerforNativeException(
+ executeDefaultHandler,
+ forceApplicationToQuit,
+ customErrorHandler
+ );
}
};
From 750b8ebc1a58f983d7c947ae7eb5a6b8ba21415f Mon Sep 17 00:00:00 2001
From: Solimando Damien
Date: Mon, 23 Jul 2018 14:53:37 +0200
Subject: [PATCH 4/6] Fix some regressions
---
.../NativeExceptionHandlerIfc.java | 2 +-
.../ReactNativeExceptionHandlerModule.java | 89 +++++++++----------
2 files changed, 45 insertions(+), 46 deletions(-)
diff --git a/android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java b/android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java
index babb49f..bdcd82e 100644
--- a/android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java
+++ b/android/src/main/java/com/masteratul/exceptionhandler/NativeExceptionHandlerIfc.java
@@ -1,5 +1,5 @@
package com.masteratul.exceptionhandler;
public interface NativeExceptionHandlerIfc {
- void handleNativeException(Thread thread, Throwable throwable);
+ void handleNativeException(Thread thread, Throwable throwable, Thread.UncaughtExceptionHandler originalHandler);
}
diff --git a/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java b/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java
index ffe47e7..00352d3 100644
--- a/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java
+++ b/android/src/main/java/com/masteratul/exceptionhandler/ReactNativeExceptionHandlerModule.java
@@ -12,7 +12,7 @@
public class ReactNativeExceptionHandlerModule extends ReactContextBaseJavaModule {
- private ReactApplicationContext reactContext;
+ private ReactApplicationContext reactContext;
private Activity activity;
private static Class errorIntentTargetClass = DefaultErrorScreen.class;
private static NativeExceptionHandlerIfc nativeExceptionHandler;
@@ -20,64 +20,63 @@ public class ReactNativeExceptionHandlerModule extends ReactContextBaseJavaModul
private Thread.UncaughtExceptionHandler originalHandler;
public ReactNativeExceptionHandlerModule(ReactApplicationContext reactContext) {
- super(reactContext);
- this.reactContext = reactContext;
- }
+ super(reactContext);
+ this.reactContext = reactContext;
+ }
- @Override
- public String getName() {
- return "ReactNativeExceptionHandler";
- }
+ @Override
+ public String getName() {
+ return "ReactNativeExceptionHandler";
+ }
- @ReactMethod
- public void setHandlerforNativeException(
- final boolean executeOriginalUncaughtExceptionHandler,
- final boolean forceToQuit,
- Callback customHandler) {
+ @ReactMethod
+ public void setHandlerforNativeException(
+ final boolean executeOriginalUncaughtExceptionHandler,
+ final boolean forceToQuit,
+ Callback customHandler) {
- callbackHolder = customHandler;
- originalHandler = Thread.getDefaultUncaughtExceptionHandler();
+ callbackHolder = customHandler;
+ originalHandler = Thread.getDefaultUncaughtExceptionHandler();
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread thread, Throwable throwable) {
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
- String stackTraceString = Log.getStackTraceString(throwable);
- callbackHolder.invoke(stackTraceString);
+ String stackTraceString = Log.getStackTraceString(throwable);
+ callbackHolder.invoke(stackTraceString);
- if (executeOriginalUncaughtExceptionHandler && originalHandler != null) {
- originalHandler.uncaughtException(thread, throwable);
- }
+ if (nativeExceptionHandler != null) {
+ nativeExceptionHandler.handleNativeException(thread, throwable, originalHandler);
+ } else {
+ activity = getCurrentActivity();
- if (nativeExceptionHandler != null) {
- nativeExceptionHandler.handleNativeException(thread, throwable);
- } else {
- activity = getCurrentActivity();
+ Intent i = new Intent();
+ i.setClass(activity, errorIntentTargetClass);
+ i.putExtra("stack_trace_string",stackTraceString);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- Intent i = new Intent();
- i.setClass(activity, errorIntentTargetClass);
- i.putExtra("stack_trace_string",stackTraceString);
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ activity.startActivity(i);
+ activity.finish();
- activity.startActivity(i);
- activity.finish();
+ if (executeOriginalUncaughtExceptionHandler && originalHandler != null) {
+ originalHandler.uncaughtException(thread, throwable);
+ }
- if (forceToQuit) {
- System.exit(0);
- }
+ if (forceToQuit) {
+ System.exit(0);
+ }
+ }
+ }
+ });
+ }
- }
- }
- });
- }
-
- public static void replaceErrorScreenActivityClass(Class errorScreenActivityClass){
- errorIntentTargetClass = errorScreenActivityClass;
- }
+ public static void replaceErrorScreenActivityClass(Class errorScreenActivityClass){
+ errorIntentTargetClass = errorScreenActivityClass;
+ }
public static void setNativeExceptionHandler(NativeExceptionHandlerIfc nativeExceptionHandler) {
ReactNativeExceptionHandlerModule.nativeExceptionHandler = nativeExceptionHandler;
}
-}
\ No newline at end of file
+}
From 703a98b8cc0ca694428f56932205bdeac69f07bf Mon Sep 17 00:00:00 2001
From: Solimando Damien
Date: Wed, 25 Jul 2018 11:27:52 +0200
Subject: [PATCH 5/6] Update README.md
---
README.md | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/README.md b/README.md
index 793c371..ac0ad89 100644
--- a/README.md
+++ b/README.md
@@ -205,6 +205,34 @@ public class MainApplication extends Application implements ReactApplication {
```
+**Modifying Android Native Exception handler** (NATIVE CODE HAS TO BE WRITTEN) *recommended that you do this in android studio*
+
+- In the `android/app/src/main/java/[...]/MainActivity.java`
+
+```java
+import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerModule;
+import com.masteratul.exceptionhandler.NativeExceptionHandlerIfc
+...
+...
+...
+public class MainApplication extends Application implements ReactApplication {
+...
+...
+ @Override
+ public void onCreate() {
+ ....
+ ....
+ ....
+ ReactNativeExceptionHandlerModule.setNativeExceptionHandler(new NativeExceptionHandlerIfc() {
+ @Override
+ public void handleNativeException(Thread thread, Throwable throwable, Thread.UncaughtExceptionHandler originalHandler) {
+ // Put your error handling code here
+ }
+ }//This will override the default behaviour of displaying the recover activity.
+ }
+
+```
+
**Modifying iOS Native Exception handler UI** (NATIVE CODE HAS TO BE WRITTEN) *recommended that you do this in XCode*
Unlike Android, in the case of iOS, there is no way to restart the app if it has crashed. Also, during a **Native_Exceptions** the UI becomes quite unstable since the exception occured on the main UI thread. Hence, none of the click or press handlers would work either.
@@ -410,6 +438,10 @@ This is specifically occuring when you use [wix library](http://wix.github.io/re
setNativeExceptionHandler(nativeErrorCallback, false);
```
+### Previously defined exception handlers are not executed anymore
+
+A lot of frameworks (especially analytics sdk's) implement global exception handlers. In order to keep these frameworks working while using react-native-exception-hanlder, you can pass a boolean value as third argument to `setNativeExceptionHandler(..., ..., true`) what will trigger the execution of the last global handler registered.
+
## CONTRIBUTORS
- [Atul R](https://github.com/master-atul)
@@ -426,6 +458,7 @@ setNativeExceptionHandler(nativeErrorCallback, false);
- [TomMahle](https://github.com/TomMahle)
- [Sébastien Krafft](https://github.com/skrafft)
- [Mark Friedman](https://github.com/mark-friedman)
+- [Damien Solimando](https://github.com/dsolimando)
## TESTING NATIVE EXCEPTIONS/ERRORS
From e03a7c4b4a7053023f230f48ecbbcd4e9b316028 Mon Sep 17 00:00:00 2001
From: Atul
Date: Fri, 27 Jul 2018 22:43:17 +0530
Subject: [PATCH 6/6] Bumped version and added readme.
---
README.md | 55 ++++++++++++++++++++++++++++++++--------------------
index.js | 52 +++++++++++--------------------------------------
package.json | 2 +-
3 files changed, 46 insertions(+), 63 deletions(-)
diff --git a/README.md b/README.md
index e7d3368..57ab44b 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,11 @@ To tackle this we register a global error handler that could be used to for exam
**V2 of this module now supports catching Unhandled Native Exceptions also along with the JS Exceptions ✌🏻🍻**
There are **NO** breaking changes. So its safe to upgrade from v1 to v2. So there is no reason not to 😉.
+**V2.9**
+- Adds support for executing previously set error handlers (now this module can work with other analytics modules)
+- Adds an improved approach for overwriting native error handlers.
+- Thanks @ [Damien Solimando](https://github.com/dsolimando)
+
**Example** repo can be found here:
*[https://github.com/master-atul/react-native-exception-handler-example](https://github.com/master-atul/react-native-exception-handler-example) *
@@ -128,7 +133,7 @@ setJSExceptionHandler((error, isFatal) => {
// or hit google analytics to track crashes
// or hit a custom api to inform the dev team.
});
-
+//=================================================
// ADVANCED use case:
const exceptionhandler = (error, isFatal) => {
// your error handler function
@@ -156,17 +161,21 @@ setNativeExceptionHandler((exceptionString) => {
//NOTE: alert or showing any UI change via JS
//WILL NOT WORK in case of NATIVE ERRORS.
});
-
+//====================================================
// ADVANCED use case:
const exceptionhandler = (exceptionString) => {
// your exception handler code here
}
-setNativeExceptionHandler(exceptionhandler,forceAppQuit);
+setNativeExceptionHandler(exceptionhandler,forceAppQuit,executeDefaultHandler);
// - exceptionhandler is the exception handler function
// - forceAppQuit is an optional ANDROID specific parameter that defines
-// if the app should be force quit on error. default value is true.
-// To see usecase check the common issues section.
-
+// if the app should be force quit on error. default value is true.
+// To see usecase check the common issues section.
+// - executeDefaultHandler is an optional boolean (both IOS, ANDROID)
+// It executes previous exception handlers if set by some other module.
+// It will come handy when you use any other crash analytics module along with this one
+// Default value is set to false. Set to true if you are using other analytics modules.
+
```
It is recommended you set both the handlers.
@@ -201,15 +210,15 @@ In Android and iOS you will see something like
-**Modifying Android Native Exception handler UI** (NATIVE CODE HAS TO BE WRITTEN) *recommended that you do this in android studio*
+**Modifying Android Native Exception handler (RECOMMENDED APPROACH)**
-- Create an Empty Activity in the `android/app/src/main/java/[...]/`. For example lets say CustomErrorDialog.java
-- Customize your activity to look and behave however you need it to be.
-- In the `android/app/src/main/java/[...]/MainApplication.java`
+(NATIVE CODE HAS TO BE WRITTEN) *recommended that you do this in android studio*
+
+- In the `android/app/src/main/java/[...]/MainActivity.java`
```java
import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerModule;
-import .YourCustomActivity; //This is your CustomErrorDialog.java
+import com.masteratul.exceptionhandler.NativeExceptionHandlerIfc
...
...
...
@@ -221,18 +230,27 @@ public class MainApplication extends Application implements ReactApplication {
....
....
....
- ReactNativeExceptionHandlerModule.replaceErrorScreenActivityClass(YourCustomActivity.class); //This will replace the native error handler popup with your own custom activity.
+ ReactNativeExceptionHandlerModule.setNativeExceptionHandler(new NativeExceptionHandlerIfc() {
+ @Override
+ public void handleNativeException(Thread thread, Throwable throwable, Thread.UncaughtExceptionHandler originalHandler) {
+ // Put your error handling code here
+ }
+ }//This will override the default behaviour of displaying the recover activity.
}
```
-**Modifying Android Native Exception handler** (NATIVE CODE HAS TO BE WRITTEN) *recommended that you do this in android studio*
+**Modifying Android Native Exception handler UI (CUSTOM ACTIVITY APPROACH (OLD APPROACH).. LEAVING FOR BACKWARD COMPATIBILITY)**
-- In the `android/app/src/main/java/[...]/MainActivity.java`
+(NATIVE CODE HAS TO BE WRITTEN) *recommended that you do this in android studio*
+
+- Create an Empty Activity in the `android/app/src/main/java/[...]/`. For example lets say CustomErrorDialog.java
+- Customize your activity to look and behave however you need it to be.
+- In the `android/app/src/main/java/[...]/MainApplication.java`
```java
import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerModule;
-import com.masteratul.exceptionhandler.NativeExceptionHandlerIfc
+import .YourCustomActivity; //This is your CustomErrorDialog.java
...
...
...
@@ -244,12 +262,7 @@ public class MainApplication extends Application implements ReactApplication {
....
....
....
- ReactNativeExceptionHandlerModule.setNativeExceptionHandler(new NativeExceptionHandlerIfc() {
- @Override
- public void handleNativeException(Thread thread, Throwable throwable, Thread.UncaughtExceptionHandler originalHandler) {
- // Put your error handling code here
- }
- }//This will override the default behaviour of displaying the recover activity.
+ ReactNativeExceptionHandlerModule.replaceErrorScreenActivityClass(YourCustomActivity.class); //This will replace the native error handler popup with your own custom activity.
}
```
diff --git a/index.js b/index.js
index 1eeb236..d1b3200 100644
--- a/index.js
+++ b/index.js
@@ -4,20 +4,10 @@ const { ReactNativeExceptionHandler } = NativeModules;
const noop = () => { };
-export const setJSExceptionHandler = (
- customHandler = noop,
- allowedInDevMode = false
-) => {
- if (
- typeof allowedInDevMode !== "boolean" ||
- typeof customHandler !== "function"
- ) {
- console.log(
- "setJSExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean"
- );
- console.log(
- "Not setting the JS handler .. please fix setJSExceptionHandler call"
- );
+export const setJSExceptionHandler = (customHandler = noop, allowedInDevMode = false) => {
+ if (typeof allowedInDevMode !== "boolean" || typeof customHandler !== "function"){
+ console.log("setJSExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean");
+ console.log("Not setting the JS handler .. please fix setJSExceptionHandler call");
return;
}
const allowed = allowedInDevMode ? true : !__DEV__;
@@ -25,42 +15,22 @@ export const setJSExceptionHandler = (
global.ErrorUtils.setGlobalHandler(customHandler);
console.error = (message, error) => global.ErrorUtils.reportError(error); // sending console.error so that it can be caught
} else {
- console.log(
- "Skipping setJSExceptionHandler: Reason: In DEV mode and allowedInDevMode = false"
- );
+ console.log("Skipping setJSExceptionHandler: Reason: In DEV mode and allowedInDevMode = false");
}
};
export const getJSExceptionHandler = () => global.ErrorUtils.getGlobalHandler();
-export const setNativeExceptionHandler = (
- customErrorHandler = noop,
- forceApplicationToQuit = true,
- executeDefaultHandler = false
-) => {
- if (
- typeof customErrorHandler !== "function" ||
- typeof forceApplicationToQuit !== "boolean"
- ) {
- console.log(
- "setNativeExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean"
- );
- console.log(
- "Not setting the native handler .. please fix setNativeExceptionHandler call"
- );
+export const setNativeExceptionHandler = (customErrorHandler = noop, forceApplicationToQuit = true, executeDefaultHandler = false) => {
+ if (typeof customErrorHandler !== "function" || typeof forceApplicationToQuit !== "boolean") {
+ console.log("setNativeExceptionHandler is called with wrong argument types.. first argument should be callback function and second argument is optional should be a boolean");
+ console.log("Not setting the native handler .. please fix setNativeExceptionHandler call");
return;
}
if (Platform.OS === "ios") {
- ReactNativeExceptionHandler.setHandlerforNativeException(
- executeDefaultHandler,
- customErrorHandler
- );
+ ReactNativeExceptionHandler.setHandlerforNativeException(executeDefaultHandler, customErrorHandler);
} else {
- ReactNativeExceptionHandler.setHandlerforNativeException(
- executeDefaultHandler,
- forceApplicationToQuit,
- customErrorHandler
- );
+ ReactNativeExceptionHandler.setHandlerforNativeException(executeDefaultHandler, forceApplicationToQuit, customErrorHandler);
}
};
diff --git a/package.json b/package.json
index bdd8e77..ce68a48 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-exception-handler",
- "version": "2.8.9",
+ "version": "2.9.0",
"description": "A react native module that lets you to register a global error handler that can capture fatal/non fatal uncaught exceptions.",
"main": "index.js",
"scripts": {