Android 中可通过实现 Thread.UncaughtExceptionHandler 捕获线程未处理的异常:

// CrashHandler.kt
import android.content.Context
import java.io.PrintWriter
import java.io.StringWriter

class CrashHandler(private val context: Context) : Thread.UncaughtExceptionHandler {
  // 保存默认异常处理器
  private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()

  override fun uncaughtException(t: Thread, e: Throwable) {
    // 收集错误信息
    val errorLog = StringBuilder()
    errorLog.append("Android 崩溃:\n线程:${t.name}\n")
    // 获取堆栈跟踪
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    e.printStackTrace(pw)
    errorLog.append("堆栈:${sw.toString()}")

    // 保存日志或上报
    print(errorLog.toString())
    ErrorReporter.report(context, errorLog.toString())

    // 调用默认处理器(触发系统崩溃提示)
    defaultHandler?.uncaughtException(t, e)
  }

  companion object {
    // 在 Application 中初始化
    fun init(context: Context) {
      Thread.setDefaultUncaughtExceptionHandler(CrashHandler(context))
    }
  }
}

// 初始化(在自定义 Application 类中)
class MyApp : Application() {
  override fun onCreate() {
    super.onCreate()
    CrashHandler.init(this)
  }
}

三、实战场景:关键业务错误处理方案

结合 React Native 开发中的高频场景,以下是针对性的错误处理实践方案,涵盖网络请求、异步操作、原生模块调用等核心环节。

(一)网络请求错误处理

网络请求是错误高发场景,需处理请求失败、响应异常、数据解析错误等问题,建议封装统一的请求工具:

import axios from 'axios';
import { Alert } from 'react-native';

// 创建 axios 实例
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
});

// 请求拦截器:添加请求头(如 Token)
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 响应拦截器:统一处理错误
api.interceptors.response.use(
  (response) => response.data, // 成功时直接返回数据
  (error) => {
    let errorMessage = '网络请求失败,请稍后重试';
    
    // 分类处理错误
    if (error.response) {
      // 服务器返回错误(4xx/5xx)
      const status = error.response.status;
      const data = error.response.data;
      errorMessage = data?.message || `请求错误(${status})`;
      
      // 特殊状态码处理(如 401 未授权)
      if (status === 401) {
        // 触发登出逻辑
        logout();
        errorMessage = '登录已过期,请重新登录';
      }
    } else if (error.request) {
      // 无响应(网络错误、超时)
      errorMessage = error.code === 'ECONNABORTED' ? '请求超时' : '网络异常,请检查网络连接';
    } else {
      // 请求配置错误(如参数错误)
      errorMessage = `请求配置错误:${error.message}`;
    }

    // 上报错误信息
    reportErrorToMonitor({
      type: 'NetworkError',
      message: errorMessage,
      stack: error.stack,
      requestConfig: error.config,
    });

    // 向用户展示错误提示
    Alert.alert('提示', errorMessage);
    
    return Promise.reject(error);
  }
);

// 使用示例:获取用户数据
const fetchUser = async (userId) => {
  try {
    const data = await api.get(`/users/${userId}`);
    return data;
  } catch (error) {
    // 业务层可额外处理(如重试、降级)
    console.error('获取用户数据失败:', error);
    throw error; // 向上传递错误,供组件处理
  }
};

(二)原生模块调用错误处理

React Native 调用自定义原生模块时,需处理参数校验、原生逻辑异常等问题,建议通过 Promise 封装原生方法,便于捕获错误:

1. 原生模块封装(以 Android 为例)
// CustomModule.kt
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise

class CustomModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
  override fun getName() = "CustomModule"

  // 用 Promise 封装原生方法,便于 JS 捕获错误
  @ReactMethod
  fun processData(input: String, promise: Promise) {
    try {
      // 校验参数
      if (input.isEmpty()) {
        throw IllegalArgumentException("输入参数不能为空")
      }
      // 业务逻辑
      val result = "处理后的结果:$input"
      promise.resolve(result) // 成功回调
    } catch (e: Exception) {
      // 错误回调:传递错误信息到 JS 层
      promise.reject("PROCESS_ERROR", e.message, e)
    }
  }
}
2. JS 层调用与错误处理
import { NativeModules } from 'react-native';
const { CustomModule } = NativeModules;

// 调用原生模块方法
const processNativeData = async (input) => {
  try {
    const result = await CustomModule.processData(input);
    return result;
  } catch (error) {
    // 捕获原生模块抛出的错误
    console.error('原生模块调用失败:', error.code, error.message);
    // 上报错误
    reportErrorToMonitor({
      type: 'NativeModuleError',
      module: 'CustomModule',
      method: 'processData',
      code: error.code,
      message: error.message,
    });
    // 向用户提示
    Alert.alert('错误', `处理失败:${error.message}`);
    throw error;
  }
};

// 使用示例
processNativeData('测试输入')
  .then((result) => console.log(result))
  .catch((error) => console.error(error));

(三)异步操作错误处理(如文件读写、存储)

React Native 中的异步操作(如 AsyncStorage、文件系统操作)需通过 try/catch 捕获错误,并提供降级方案:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { Alert } from 'react-native';

// 封装 AsyncStorage 操作,统一处理错误
const StorageService = {
  async setItem(key, value) {
    try {
      const jsonValue = JSON.stringify(value);
      await AsyncStorage.setItem(key, jsonValue);
    } catch (error) {
      console.error(`存储 ${key} 失败:`, error);
      // 上报错误
      reportErrorToMonitor({
        type: 'StorageError',
        operation: 'setItem',
        key,
        message: error.message,
      });
      // 提示用户
      Alert.alert('存储错误', '数据保存失败,请检查存储空间');
      throw error;
    }
  },

  async getItem(key) {
    try {
      const jsonValue = await AsyncStorage.getItem(key);
      return jsonValue != null ? JSON.parse(jsonValue) : null;
    } catch (error) {
      console.error(`获取 ${key} 失败:`, error);
      reportErrorToMonitor({
        type: 'StorageError',
        operation: 'getItem',
        key,
        message: error.message,
      });
      // 降级处理:返回默认值
      return null;
    }
  },
};

[图例插入标识:React Native 异步操作错误处理流程示意图] 流程节点:发起异步操作(setItem/getItem)→ try 块执行操作 → 成功:返回结果 / 失败:catch 捕获 → 错误上报 → 用户提示 / 降级处理

四、错误监控与日志上报

仅在应用内处理错误不够,还需建立完善的监控体系,实时收集错误信息,以便定位问题并优化。常用方案分为 “自建监控” 和 “第三方监控” 两类。

(一)第三方监控工具(推荐)

第三方工具已封装好 JS 层与原生层的错误捕获逻辑,支持崩溃分析、用户行为追踪、设备信息收集等功能,主流工具包括:

1. Sentry
  • 支持 React Native 全平台错误捕获(JS 错误、原生崩溃)。
  • 提供详细的堆栈跟踪、错误上下文(用户信息、设备信息、应用版本)。
  • 支持错误分组、告警通知(邮件、Slack)。
集成示例
// 安装依赖:npm install @sentry/react-native && npx pod-install ios
import * as Sentry from '@sentry/react-native';

// 初始化 Sentry(在 App 入口处)
Sentry.init({
  dsn: '你的 Sentry DSN',
  environment: __DEV__ ? 'development' : 'production',
  tracesSampleRate: 1.0, // 性能监控采样率
});

// 手动上报错误(可选)
try {
  // 可能出错的逻辑
} catch (error) {
  Sentry.captureException(error, {
    extra: { customInfo: '额外上下文信息' },
    tags: { module: 'user', action: 'login' },
  });
}
2. Bugsnag
  • 专注于移动应用崩溃监控,支持 React Native 双端原生崩溃捕获。
  • 提供错误优先级分级、用户会话跟踪、版本趋势分析。
3. Firebase Crashlytics
  • 与 Firebase 生态集成,适合使用 Firebase 的项目。
  • 免费版功能足够满足中小项目需求,支持崩溃统计与过滤。

(二)自建监控系统

若需定制化监控逻辑,可通过以下方式实现:

  1. 日志收集:在 JS 层和原生层捕获错误后,将错误日志(含错误信息、堆栈、设备信息、用户 ID)保存到本地。
  2. 日志上报:在应用下次启动时,检查本地日志,将未上报的错误通过网络请求发送到自建服务器。
  3. 后台管理:搭建后台系统,对错误日志进行分类、统计、搜索,设置告警规则(如某类错误发生率超过 5% 时触发告警)。

五、错误处理最佳实践

(一)开发阶段最佳实践

  1. 启用严格模式:在 App.js 中启用 React 严格模式,提前发现潜在问题:
import { StrictMode } from 'react';
import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';

AppRegistry.registerComponent(appName, () => () => <StrictMode><App /></StrictMode>);
  1. 禁用生产环境的 RedBox/YellowBox:避免向用户暴露错误细节,保护敏感信息。
  2. 编写错误处理测试:使用 Jest 测试错误边界、异常捕获逻辑,确保其能正常工作。

(二)生产阶段最佳实践

  1. 避免静默失败:所有异步操作、原生模块调用必须添加错误处理,禁止忽略 catch 块。
  2. 提供友好的用户提示:避免向用户展示技术术语(如 “NullPointerException”),用通俗语言说明问题(如 “数据加载失败,请检查网络”)。
  3. 实现错误降级:核心功能出错时,提供替代方案(如网络请求失败时展示缓存数据)。
  4. 定期分析错误日志:优先修复高频错误、严重崩溃(如启动时崩溃),持续优化应用稳定性。
  5. 版本控制错误处理逻辑:记录错误处理代码的变更,便于回溯问题。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐