我的编程空间,编程开发者的网络收藏夹
学习永远不晚

ReactNative错误采集原理在Android中如何实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

ReactNative错误采集原理在Android中如何实现

今天小编给大家分享一下ReactNative错误采集原理在Android中如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

1 JS错误

1.1 Error

Error是错误基类,其他错误继承自Error,Error对象有两个主要属性,name和message

new Error(message)

1.2 常见的错误

SyntaxError:语法错误

语法错误是一种常见的错误,在所有编程语言中都存在,表示不符合编程语言规范。

一类是词法、语法分析转换生成语法树时发生,此类异常一旦发生,导致整个js文件无法执行,而其他异常发生在代码运行时,在错误出现的那一行之前的代码不受影响

const 1xx; // SyntaxError

另一类是运行中出现的语法错误,如开发中常见的json解析错误,参数传入非标准json字符

JSON.parse('') // SyntaxError: Unexpected end of JSON input
ReferenceError:引用错误

引用了一个不能存在的变量,变量未声明就引用了

const a = xxx; // ReferenceError: xxx is not defined
TypeError:类型错误

变量或参数不是有效类型

1() // TypeError: 1 is not a functionconst a = new 111() // TypeError: 111 is not a constructor
RangeError:边界错误

超出有效范围时发生异常,常见的是数组长度超出范围

[].length = -1 // RangeError: Invalid array length
URIError:URI错误

调用URI相关函数中出现,包括encodeURI、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()

decodeURI('%') // URIError: URI malformed

1.3 自定义错误

我们可以继承Error类,实现自定义的错误

class MyError extends Error {    constructor(message) {        super(message);        this.name = 'MyError';    }}function() {  throw new MyError('error message'); // MyError: error message}

2 RN错误处理

RN错误处理包括JS和native两部分,由JS捕获,抛给Native处理

2.1 JS部分

2.1.1 MessageQueue

Native和JS通信的消息队列, 负责Native和JS通讯, 包括渲染、交互、各种互相调用等。所有的通信都会经过_guard函数处理,在_guard中会被try-catch住,出现异常后调用ErrorUtils处理

__guard(fn: () => void) {    if (this.__shouldPauseOnThrow()) {      fn();    } else {      try {        fn();      } catch (error) {        ErrorUtils.reportFatalError(error); // 捕获异常,交给ErrorUtils      }    }  }

注:react-native/Libraries/BatchedBridge/MessageQueue.js

2.1.2 ErrorUtils

ErrorUtils用于处理RN中所有的异常,它对暴露异常处理拦截接口

  • 异常上报

收到异常后调用_globalHandler处理异常

// 处理非fatal异常reportError(error: mixed): void {    _globalHandler && _globalHandler(error, false);},// 处理fatal异常reportFatalError(error: mixed): void {    _globalHandler && _globalHandler(error, true); },
  • 异常处理

所有异常通过_globalHandle函数处理,默认情况下_globalHandler会直接将错误抛出,ErrorUtils对外提供了setGlobalHanlder做错误拦截处理,RN重写_globalHandler来做错误收集和处理

let _globalHandler: ErrorHandler = function onError(  e: mixed,  isFatal: boolean,) {  throw e;};setGlobalHandler(fun: ErrorHandler): void {    _globalHandler = fun;},getGlobalHandler(): ErrorHandler {    return _globalHandler;},

注:react-native/Libraries/polyfills/error-guard.js

2.1.3 ExceptionsManager

ExceptionsManager是RN中异常管理模块,负责红屏处理、console.error、并将异常传给Native侧

异常处理器设置

  • 调用ErrorUtils.setGlobalHandler,把错误处理实现交给ExceptionsManager.handleException

  • console.error处理:调用ExceptionsManager.installConsoleErrorReporter重写console.error

const ExceptionsManager = require('./ExceptionsManager');// Set up console.error handlerExceptionsManager.installConsoleErrorReporter();// Set up error handlerif (!global.__fbDisableExceptionsManager) {  const handleError = (e, isFatal) => {    try {      ExceptionsManager.handleException(e, isFatal);    } catch (ee) {      console.log('Failed to print error: ', ee.message);      throw e;    }  };  const ErrorUtils = require('../vendor/core/ErrorUtils');  ErrorUtils.setGlobalHandler(handleError);}

注:react-native/Libraries/Core/setUpErrorHandling.js

ExceptionsManager处理异常

  • 构建Error:如果错误不是Error类型,构造一个SyntheticError,方便日志输出和展示

function handleException(e: mixed, isFatal: boolean) {  let error: Error;  if (e instanceof Error) {    error = e;  } else {    error = new SyntheticError(e);  }  reportException(error, isFatal);}
  • 调用错误处理

function reportException(e: ExtendedError, isFatal: boolean) {  const NativeExceptionsManager = require('./NativeExceptionsManager').default;  if (NativeExceptionsManager) {    // 解析错误,获取错误信息、堆栈    const parseErrorStack = require('./Devtools/parseErrorStack');    const stack = parseErrorStack(e);    const currentExceptionID = ++exceptionID;    const originalMessage = e.message || '';    let message = originalMessage;    if (e.componentStack != null) {      message += `\n\nThis error is located at:${e.componentStack}`;    }    const namePrefix = e.name == null || e.name === '' ? '' : `${e.name}: `;    const isFromConsoleError = e.name === 'console.error';    if (!message.startsWith(namePrefix)) {      message = namePrefix + message;    }    // 如果是console.error则输出    if (!isFromConsoleError) {      if (console._errorOriginal) {        console._errorOriginal(message);      } else {        console.error(message);      }    }    message =      e.jsEngine == null ? message : `${message}, js engine: ${e.jsEngine}`;    // 抑制(不展示)红屏,不展示native红屏弹窗,forceRedbox默认为false    const isHandledByLogBox =      e.forceRedbox !== true && global.__unstable_isLogBoxEnabled === true;    const data = preprocessException({      message,      originalMessage: message === originalMessage ? null : originalMessage,      name: e.name == null || e.name === '' ? null : e.name,      componentStack:        typeof e.componentStack === 'string' ? e.componentStack : null,      stack,      id: currentExceptionID,      isFatal,      extraData: {        jsEngine: e.jsEngine,        rawStack: e.stack,        // Hack to hide native redboxes when in the LogBox experiment.        // This is intentionally untyped and stuffed here, because it is temporary.        suppressRedBox: isHandledByLogBox,      },    });    // 如果抑制native红屏,展示JS红屏提示错误    if (isHandledByLogBox) {      LogBoxData.addException({        ...data,        isComponentError: !!e.isComponentError,      });    }    // 把调用NativeExceptionsManager上报给native    NativeExceptionsManager.reportException(data);  }}
  • NativeExceptionsManager调用native模块上报错误

// Native导出类,以Android为例,对应ExceptionsManagerModule.javaconst NativeModule = TurboModuleRegistry.getEnforcing<Spec>(  'ExceptionsManager',);const ExceptionsManager{  // 判断是否是fatal调用不同函数上报reportException(data: ExceptionData): void {    if (data.isFatal) {      ExceptionsManager.reportFatalException(data.message, data.stack, data.id);    } else {      ExceptionsManager.reportSoftException(data.message, data.stack, data.id);    }  },   // 上报fatal异常 reportFatalException(    message: string,    stack: Array<StackFrame>,    exceptionId: number,  ) {    NativeModule.reportFatalException(message, stack, exceptionId);  },  // 上报soft异常reportSoftException(    message: string,    stack: Array<StackFrame>,    exceptionId: number,  ) {    NativeModule.reportSoftException(message, stack, exceptionId);  },  // Android提供关闭红屏函数dismissRedbox(): void {    if (Platform.OS !== 'ios' && NativeModule.dismissRedbox) {      // TODO(T53311281): This is a noop on iOS now. Implement it.      NativeModule.dismissRedbox();    }  },}

console.error处理

上述提到调用ExceptionsManager.installConsoleErrorReporter处理console.error,处理成非fatal异常

function installConsoleErrorReporter() {  // 如果设置过,return  if (console._errorOriginal) {    return; // already installed  }  console._errorOriginal = console.error.bind(console);  // 设置console.error处理函数  console.error = reactConsoleErrorHandler;  if (console.reportErrorsAsExceptions === undefined) {    console.reportErrorsAsExceptions = true;  }}// console.error处理函数,最终调用reportException上报成非fatal异常function reactConsoleErrorHandler() {  if (arguments[0] && arguments[0].stack) {    // 上报    reportException(arguments[0],  false);  } else {    // 构造一个SyntheticError    const stringifySafe = require('../Utilities/stringifySafe');    const str = Array.prototype.map      .call(arguments, value =>        typeof value === 'string' ? value : stringifySafe(value),      )      .join(' ');    const error: ExtendedError = new SyntheticError(str);    error.name = 'console.error';// 上报    reportException(error,  false);  }}

注:react-native/Libraries/Core/ExceptionsManager.js

注:跟进上述源码可知,红屏是通过isHandledByLogBox参数可以禁止native红屏弹窗,isHandledByLogBox是通过global.__unstable_isLogBoxEnabled控制,可以通过下面方式禁止native红屏展示,但是还是会展示js红屏来提示错误

global.__unstable_isLogBoxEnabled = true;YellowBox.__unstable_enableLogBox(); // 内部调用了上面的代码

2.2 Native部分

2.2.1 ExceptionsManagerModule

上面讲述了JS处理异常后将异常抛给native处理,ExceptionsManagerModule是native处理异常模块,导出给JS类名为ExceptionsManager

ExceptionsManagerModule异常处理

// 上报fatal异常@ReactMethodpublic void reportFatalException(String message, ReadableArray stack, int id) {    JavaOnlyMap data = new JavaOnlyMap();    data.putString("message", message);    data.putArray("stack", stack);    data.putInt("id", id);    data.putBoolean("isFatal", true);    reportException(data);}// 上报soft异常@ReactMethodpublic void reportSoftException(String message, ReadableArray stack, int id) {    JavaOnlyMap data = new JavaOnlyMap();    data.putString("message", message);    data.putArray("stack", stack);    data.putInt("id", id);    data.putBoolean("isFatal", false);    reportException(data);}// 最终调用reportException@ReactMethodpublic void reportException(ReadableMap data) {  // 错误堆栈    String message = data.hasKey("message") ? data.getString("message") : "";    ReadableArray stack = data.hasKey("stack") ? data.getArray("stack") : Arguments.createArray();    int id = data.hasKey("id") ? data.getInt("id") : -1;    boolean isFatal = data.hasKey("isFatal") ? data.getBoolean("isFatal") : false;  // dev模式,展示红屏dialog    if (mDevSupportManager.getDevSupportEnabled()) {      // 获取是否抑制红屏参数,对应js侧传入的isHandledByLogBox      boolean suppressRedBox = false;      if (data.getMap("extraData") != null && data.getMap("extraData").hasKey("suppressRedBox")) {        suppressRedBox = data.getMap("extraData").getBoolean("suppressRedBox");      }      if (!suppressRedBox) {        mDevSupportManager.showNewJSError(message, stack, id); // 显示红屏弹窗      }    } else {// fatal抛出JavascriptException异常,非fatal打印出来      if (isFatal) {        throw new JavascriptException(jsStackTrace)          .setExtraDataAsJson(extraDataAsJson);      } else {        logException(jsStackTrace, extraDataAsJson);      }    }}@ReactMethodpublic void dismissRedbox() {    if (mDevSupportManager.getDevSupportEnabled()) {      mDevSupportManager.hideRedboxDialog();    }}
// 上报soft异常- (void)reportSoft: (NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(double)exceptionId suppressRedBox: (BOOL) suppressRedBox {    if (!suppressRedBox) {        [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)];    }    if (_delegate) {      [_delegate handleSoftJSExceptionWithMessage:message stack:stack exceptionId:[NSNumber numberWithDouble:exceptionId]];    }}// 上报fatal异常- (void)reportFatal: (NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(double)exceptionId suppressRedBox: (BOOL) suppressRedBox {    if (!suppressRedBox) {        [_bridge.redBox showErrorMessage:message withStack:stack errorCookie:((int)exceptionId)];    }    if (_delegate) {      [_delegate handleFatalJSExceptionWithMessage:message stack:stack exceptionId:[NSNumber numberWithDouble:exceptionId]];    }    static NSUInteger reloadRetries = 0;    if (!RCT_DEBUG && reloadRetries < _maxReloadAttempts) {      reloadRetries++;      RCTTriggerReloadCommandListeners(@"JS Crash Reload");    } else if (!RCT_DEV || !suppressRedBox) {      NSString *description = [@"Unhandled JS Exception: " stringByAppendingString:message];      NSDictionary *errorInfo = @{ NSLocalizedDescriptionKey: description, RCTJSStackTraceKey: stack };      RCTFatal([NSError errorWithDomain:RCTErrorDomain code:0 userInfo:errorInfo]);    }}// reportExceptionRCT_EXPORT_METHOD(reportException:(JS::NativeExceptionsManager::ExceptionData &)data){  NSString *message = data.message();  double exceptionId = data.id_();  id<NSObject> extraData = data.extraData();  // Reserialize data.stack() into an array of untyped dictionaries.  // TODO: (moti) T53588496 Replace `(NSArray<NSDictionary *> *)stack` in  // reportFatalException etc with a typed interface.  NSMutableArray<NSDictionary *> *stackArray = [NSMutableArray<NSDictionary *> new];  for (auto frame: data.stack()) {    NSMutableDictionary * frameDict = [NSMutableDictionary new];    if (frame.column().hasValue()) {      frameDict[@"column"] = @(frame.column().value());    }    frameDict[@"file"] = frame.file();    if (frame.lineNumber().hasValue()) {        frameDict[@"lineNumber"] = @(frame.lineNumber().value());    }    frameDict[@"methodName"] = frame.methodName();    if (frame.collapse().hasValue()) {        frameDict[@"collapse"] = @(frame.collapse().value());    }    [stackArray addObject:frameDict];  }  NSDictionary *dict = (NSDictionary *)extraData;  BOOL suppressRedBox = [[dict objectForKey:@"suppressRedBox"] boolValue];  if (data.isFatal()) {    [self reportFatal:message stack:stackArray exceptionId:exceptionId suppressRedBox:suppressRedBox];  } else {    [self reportSoft:message stack:stackArray exceptionId:exceptionId suppressRedBox:suppressRedBox];  }}

问题:fatal错误抛出异常后为什么应用为什么没有退出呢?

DevSupportManager处理红屏

 @Override  public void showNewJavaError(@Nullable String message, Throwable e) {    FLog.e(ReactConstants.TAG, "Exception in native call", e);    showNewError(        message, StackTraceHelper.convertJavaStackTrace(e), JAVA_ERROR_COOKIE, ErrorType.NATIVE);  }// 展示红屏弹窗private void showNewError(      @Nullable final String message,      final StackFrame[] stack,      final int errorCookie,      final ErrorType errorType) {    UiThreadUtil.runOnUiThread(        new Runnable() {          @Override          public void run() {            if (mRedBoxDialog == null) {              Activity context = mReactInstanceManagerHelper.getCurrentActivity();              mRedBoxDialog = new RedBoxDialog(context, DevSupportManagerImpl.this, mRedBoxHandler);            }            if (mRedBoxDialog.isShowing()) {              return;            }            Pair<String, StackFrame[]> errorInfo = processErrorCustomizers(Pair.create(message, stack));            mRedBoxDialog.setExceptionDetails(errorInfo.first, errorInfo.second);            mRedBoxDialog.resetReporting();            mRedBoxDialog.show();          }        });  }
2.2.2 线程异常捕获(Android)

Handle捕获异常

RN引擎创建的时候会初始化三个线程,UiThread、NativeModulesThread、JSThread,这些线程通过MessageQueueThreadHandler处理消息队列,MessageQueueThreadHandler重写了Handle的dispatchMessage函数,函数通过try-catch包裹防止应用直接退出,出现异常时调用QueueThreadExceptionHandler处理(引擎实现此接口),这里能拦截所有的异常,包括上述js捕获传到native手动抛出的、yoga布局过程中的等等

public class MessageQueueThreadHandler extends Handler {  private final QueueThreadExceptionHandler mExceptionHandler;  public MessageQueueThreadHandler(Looper looper, QueueThreadExceptionHandler exceptionHandler) {    super(looper);    mExceptionHandler = exceptionHandler;  }  @Override  public void dispatchMessage(Message msg) {    try {      super.dispatchMessage(msg);    } catch (Exception e) {      mExceptionHandler.handleException(e);    }  }}

引擎处理异常

在引擎(CatalystInstanceImpl)的内部类NativeExceptionHandler中,实现了QueueThreadExceptionHandler接口,在引擎创建时初始化,出现异常时调用NativeModuleCallExceptionHandler处理,并销毁引擎

// 内部类实现QueueThreadExceptionHandler,叫异常交给引擎的onNativeException处理private static class NativeExceptionHandler implements QueueThreadExceptionHandler {    @Override    public void handleException(Exception e) {      if (ReactFeatureFlags.enableCatalystCleanupFix) {        CatalystInstanceImpl catalystInstance = mCatalystInstanceImplWeak.get();        if (catalystInstance != null) {          catalystInstance.onNativeException(e);        }      } else {        mCatalystInstanceImpl.onNativeException(e);      }    }  }// 调用NativeModuleCallExceptionHandler处理异常,并销毁引擎private void onNativeException(Exception e) {    mHasNativeError.set(true);    boolean isAlive = !mDestroyed;    if (isAlive) {      mNativeModuleCallExceptionHandler.handleException(e);    }    mReactQueueConfiguration      .getUIQueueThread()      .runOnQueue(        new Runnable() {          @Override          public void run() {            // 销毁引擎            destroy(() -> {              if (mDestroyFinishedCallback != null) {                mDestroyFinishedCallback.onDestroyFinished();                mDestroyFinishedCallback = null;              }            });          }        });  }

注:com.facebook.react.bridge.CatalystInstanceImpl(引擎实现类)

2.2.3 最终的异常处理

默认处理方式

上述讲到引擎捕获异常后会调用NativeModuleCallExceptionHandler.handleException处理,它是个接口,引擎提供了默认实现类,默认实现类收到异常后是直接抛出,会导致应用退出

public interface NativeModuleCallExceptionHandler {    void handleException(Exception e);  void handleCaughtException(Exception e);}// 默认实现类public class DefaultNativeModuleCallExceptionHandler implements NativeModuleCallExceptionHandler {  @Override  public void handleException(Exception e) {    if (e instanceof RuntimeException) {      // Because we are rethrowing the original exception, the original stacktrace will be      // preserved.      throw (RuntimeException) e;    } else {      throw new RuntimeException(e);    }  }  @Override  public void handleCaughtException(Exception e) {    e.printStackTrace();  }}

自定义异常处理

为了防止默认处理方式将异常直接抛出导致crash,业务可以实现自定义的NativeModuleCallExceptionHandler接口来处理异常,将异常上报,并展示错误兜底页面

3 整体流程

基于上述源码解析可知,RN错误采集流程由JS侧中MessageQueue发起,经过一系列处理和封装,传到native侧,再经过native一系列转发,最终交给由引擎(CatalyInstanceImple)处理,整体流程如下图所示

ReactNative错误采集原理在Android中如何实现

4 错误兜底

页面出现异常后,对异常状态兜底是一种保障线上质量的常规手段。当页面发生严重 JS 错误(FatalError)时,会展示错误页面无法继续使用。这种方式在一些业务场景下并不友好。比如:页面上某一个次要模块发生异常,并不影响核心功能的使用,这种情况下展示出错页面有些不必要

React 16 中引入了一个新概念&mdash;&mdash;错误边界(Error Boundaries)。错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界能在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误

基于这个特性,业务能够自定义控制接收到JSError的行为,能更优雅地处理错误兜底及展示

4.1 什么是错误边界

4.1.1 概念

错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JS 错误,并且它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界能在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误

4.1.2 错误边界的关键模块

错误边界是通过 try-catch 方式捕获异常的,它在哪里进行捕获异常的呢?React 有三个重要组成模块,错误边界在 Reconciliation 中对异常进行捕获。

React基础模块(这个模块定义了React的基础API及组件相关内容。对应我们开发页面时引入的 'react' 模块)

渲染模块(这个模块对于不同类型的应用,采用不同的渲染方式。对应我们开发页面时引入的 'react-dom' 模块)

Reconciliation 模块(又叫“协调模块”,这个模块是上面两个模块的基础,主要负责任务协调、生命周期函数管理等)

4.1.3 Reconciliation介绍

Reconciliation模块是React三个重要模块之一,又叫“协调模块”,这个模块是上面两个模块的基础,主要负责任务协调、生命周期函数管理等,它分为render和commit两个阶段

  • render阶段:简单来说就是找到需要更新的工作,通过 Diff Fiber Tree 找出要做的更新工作,这是一个js计算过程,计算结果可以被缓存,计算过程可以被打断,也可以恢复执行。

  • commit阶段:提交更新并调用对应渲染模块(react-dom)进行渲染,为了防止页面抖动,该过程是同步且不能被打断

// Reconciliation阶段开始,render阶段,performSyncWorkOnRoot(同步更新)、performConcurrentWorkOnRoot(异步)function performSyncWorkOnRoot(root) {      do {        try {          workLoopSync();          break;        } catch (thrownValue) {          handleError(root, thrownValue);        }      } while (true);}function handleError(root, thrownValue) {  do {    try {      throwException(        root,        workInProgress.return,        workInProgress,        thrownValue,        renderExpirationTime      );      workInProgress = completeUnitOfWork(workInProgress);    } catch (yetAnotherThrownValue)       thrownValue = yetAnotherThrownValue;      continue;    } // Return to the normal work loop.    return;  } while (true);}function throwException(  root,  returnFiber,  sourceFiber,  value,  renderExpirationTime) {     case ClassComponent:          var _update2 = createClassErrorUpdate(            workInProgress,            errorInfo,            renderExpirationTime          );          enqueueCapturedUpdate(workInProgress, _update2);          return;        }}function createClassErrorUpdate(fiber, errorInfo, expirationTime) {  var update = createUpdate(expirationTime, null);  update.tag = CaptureUpdate;  var getDerivedStateFromError = fiber.type.getDerivedStateFromError;  if (typeof getDerivedStateFromError === "function") {    var error = errorInfo.value;    update.payload = function() {      logError(fiber, errorInfo);      return getDerivedStateFromError(error);    };  }  var inst = fiber.stateNode;  if (inst !== null && typeof inst.componentDidCatch === "function") {    update.callback = function callback() {      {        markFailedErrorBoundaryForHotReloading(fiber);      }      if (typeof getDerivedStateFromError !== "function") {        markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined        logError(fiber, errorInfo);      }      var error = errorInfo.value;      var stack = errorInfo.stack;      this.componentDidCatch(error, {        componentStack: stack !== null ? stack : ""      });      {        if (typeof getDerivedStateFromError !== "function") {          !(fiber.expirationTime === Sync)            ? warningWithoutStack$1(                false,                "%s: Error boundaries should implement getDerivedStateFromError(). " +                  "In that method, return a state update to display an error message or fallback UI.",                getComponentName(fiber.type) || "Unknown"              )            : void 0;        }      }    };  } else {    update.callback = function() {      markFailedErrorBoundaryForHotReloading(fiber);    };  }  return update;}

注:源码react-native/Libraries/Renderer/ReactFabric-dev.js

错误边界不支持hooks组件,因为错误边界的实现借助了this.setState可以传递callback的特性,useState无法传入回调,所以无法完全对标

4.2 错误边界的使用

4.2.1 如何定义一个错误边界

前面提到错误边界捕获异常之后会交给特定的方法处理,如果一个组件重写了特定的方法,这个组件就是一个错误边界组件。

定义:如果一个类组件定义了生命周期方法中的任何一个(或两个)static getDerivedStateFromError() 或 componentDidCatch(),那么它就成了一个错误边界。 使用static getDerivedStateFromError()在抛出错误后渲染回退UI。 使用 componentDidCatch() 来记录错误信息。如下:

export class ErrorBoundary extends Component<IProps, IState> {    constructor(props) {        super(props);        this.state = {            hasError: false        };    }        static getDerivedStateFromError(_error) {        return {            hasError: true        };    }        componentDidCatch(error: Error) {        // 上报错误    }    render() {        if (this.state.hasError) {            return <Text style={style.errorDesc}>出错了</Text>;        }        return this.props.children;    }}
4.2.2 如何使用错误边界

将要捕获的组件用错误边界组件包裹

export default class Example extends PureComponent<Props, State> {    render() {        return <View style={ styles.container }>            <ErrorBoundary>                {                    this.renderErrorBlock()                }            </ErrorBoundary>            <Text style={ styles.other }>other block</Text>        </View>;    }    renderErrorBlock = () => {        return <View style={ styles.errorBoundary }>            '' && <Text style={ styles.error }>error block</Text>        </View>;    }}

4.3 适用范围

4.3.1 错误边界不能捕获哪些异常
  • 事件处理:点击事件

  • 异步代码:setTimeout 或 requestAnimationFrame 回调函数等

  • 错误边界自身抛出的错误

4.3.2 建议使用场景
  • 将影响整体页面展示逻辑的模块使用错误边界包裹并设置宽高,防止其他模块计算出错

  • 将非核心模块包裹,保障在非核心模块出错时核心模块展示不受影响

  • 包裹外部依赖的组件,防止意外的错误

  • 包裹独立展示模块,如广告,活动弹窗等

以上就是“ReactNative错误采集原理在Android中如何实现”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

ReactNative错误采集原理在Android中如何实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

ReactNative错误采集原理在Android中如何实现

今天小编给大家分享一下ReactNative错误采集原理在Android中如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下
2023-07-05

ReactNative错误采集原理在Android中实现详解

这篇文章主要为大家介绍了ReactNative错误采集原理在Android中实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-28

SQL Server中如何实现错误处理

今天小编给大家分享一下SQL Server中如何实现错误处理的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、SQLServ
2023-06-30

golang函数中的错误处理是如何实现的?

go 语言中函数错误处理通过返回一个 error 接口类型的错误来实现。1. 内建的 error 接口类型的 error() 方法返回错误的描述字符串。2. 通过 if err != nil 比较错误是否为空来处理错误。3. 错误处理适用于
golang函数中的错误处理是如何实现的?
2024-05-23

如何在 PHP 应用程序中实现优雅的错误页面?

如何实现优雅的 php 错误页面:使用 php 错误处理函数(set_error_handler()):捕获并显示错误信息。自定义错误处理:定义一个处理函数,格式化并显示错误信息。设置自定义处理函数:使用 set_error_handler
如何在 PHP 应用程序中实现优雅的错误页面?
2024-05-09

如何在PHP项目中实现日志记录和错误追踪?

如何在PHP项目中实现日志记录和错误追踪?在开发PHP项目过程中,日志记录和错误追踪是非常重要的功能。通过记录日志,我们可以及时追踪和排查项目中的问题,同时也方便后续的错误分析和修复。本文将介绍如何在PHP项目中实现日志记录和错误追踪的方法
如何在PHP项目中实现日志记录和错误追踪?
2023-11-03

如何在PHP微服务中实现分布式故障和容错处理

在现代的分布式系统中,故障和容错是不可避免的问题。特别是在微服务架构中,每个服务都是相对独立的,它们之间的通信是通过网络进行的,这使得系统更加复杂,也更容易发生故障。为了保证系统的稳定性和可靠性,我们需要在PHP微服务中实现分布式故障和容错
2023-10-21

Java中的Spring框架是如何进行依赖注入的?(在Spring框架中,依赖注入的实现原理是什么?)

Spring框架中的依赖注入(DI)通过反射和注解,以及IoC容器实现。DI允许组件从外部接收依赖关系,提高了模块化和可维护性,降低了错误风险,并支持松散耦合。具体步骤包括配置bean、创建IoC容器、解析依赖关系、实例化bean和注入依赖关系。DI好处包括解耦组件、提高可测试性、减少错误、支持松散耦合和提高灵活性。
Java中的Spring框架是如何进行依赖注入的?(在Spring框架中,依赖注入的实现原理是什么?)
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录