Skip to content

Commit

Permalink
Merge pull request #75 from master-atul/develop
Browse files Browse the repository at this point in the history
Merging test branch
  • Loading branch information
a7ul authored Jul 27, 2018
2 parents f7bac35 + e03a7c4 commit a434614
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 51 deletions.
60 changes: 53 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) *

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -201,7 +210,39 @@ In Android and iOS you will see something like
<img src="https://github.com/master-atul/react-native-exception-handler/raw/master/screens/ios_native_exception.png" width="300"/>
</p>

**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)**

(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 Android Native Exception handler UI (CUSTOM ACTIVITY APPROACH (OLD APPROACH).. LEAVING FOR BACKWARD COMPATIBILITY)**

(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.
Expand Down Expand Up @@ -431,6 +472,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)
Expand All @@ -447,6 +492,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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.masteratul.exceptionhandler;

public interface NativeExceptionHandlerIfc {
void handleNativeException(Thread thread, Throwable throwable, Thread.UncaughtExceptionHandler originalHandler);
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@

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 Activity activity;
private static Class errorIntentTargetClass = DefaultErrorScreen.class;
private static NativeExceptionHandlerIfc nativeExceptionHandler;
private Callback callbackHolder;
private Thread.UncaughtExceptionHandler originalHandler;

Expand All @@ -29,39 +31,52 @@ public String getName() {


@ReactMethod
public void setHandlerforNativeException(final boolean forceToQuit, Callback customHandler){
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 (nativeExceptionHandler != null) {
nativeExceptionHandler.handleNativeException(thread, throwable, originalHandler);
} else {
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) {
if (executeOriginalUncaughtExceptionHandler && originalHandler != null) {
originalHandler.uncaughtException(thread, throwable);
}

if (forceToQuit) {
System.exit(0);
System.exit(0);
}
}
}
});
}

public static void replaceErrorScreenActivityClass(Class errorScreenActivityClass){
errorIntentTargetClass = errorScreenActivityClass;
}

public static void setNativeExceptionHandler(NativeExceptionHandlerIfc nativeExceptionHandler) {
ReactNativeExceptionHandlerModule.nativeExceptionHandler = nativeExceptionHandler;
}
}
29 changes: 14 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
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');
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__;
if (allowed) {
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);
}
};

Expand Down
Loading

0 comments on commit a434614

Please sign in to comment.