文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-07-05 06:59

关注

今天小编给大家分享一下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侧

异常处理器设置

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处理异常

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);  }}
// 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两个阶段

// 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 错误边界不能捕获哪些异常
4.3.2 建议使用场景

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

阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯