移动应用中的 Non-Fatal Error — 本质、类型及错误处理

作者: IT Sectr 发布日期: 2026-05-27 阅读时间: 8 分钟

Non-Fatal Error — 是一种不会导致应用程序终止并允许继续执行程序的错误。与 fatal error 不同,非致命错误可以在不丢失用户会话的情况下被捕获、处理和记录。根据 Firebase Crashlytics Documentation, 2024 的数据,生产应用程序中记录的所有错误中约 70% 是非致命错误,但忽视它们会导致技术债务累积和用户体验逐渐恶化。正确处理 non-fatal 错误是移动开发人员的关键技能之一。

要点总结

  • Non-Fatal Error — 不终止应用程序且允许恢复执行的错误
  • 处理 非致命错误包括 try-catch、记录和显示后备 UI
  • 记录 non-fatal 错误对于在生产环境中查找隐藏的 bug 至关重要
  • Fatal Error — 相反:导致应用程序崩溃且无法恢复的错误
  • Crashlytics 和 Sentry 允许实时追踪 non-fatal 错误

什么是 Non-Fatal Error

Non-Fatal Error — 是一种不会导致进程终止的异常或错误状态。应用程序继续运行,但可能处于不正确状态:数据未加载、请求未发送、界面元素未显示。用户要么没有注意到错误,要么看到消息后继续使用应用程序。

关键特征

非致命错误总是给程序留下恢复路径。错误处理器 可以提供替代数据、重试操作或显示界面占位符。主要任务是防止崩溃并将用户体验维持在可接受的水平。开发人员必须在每个 catch 块中明确预见到恢复场景。

在应用程序稳定性中的作用

根据 Instabug 2024 的数据,65% 的用户在两次失败交互后会删除应用程序。被忽视的 non-fatal 错误会累积并降低整体工作质量。系统性地记录和修复非致命错误是提高留存率和改善应用商店用户评价的直接途径。

非致命错误的类型

网络错误 — 是移动应用中最常见的 non-fatal 错误类型。连接超时、网络丢失、服务器状态码错误——所有这些情况都会被捕获并处理而不会崩溃。用户会看到服务不可用的消息,并提示重试。对于网络错误,典型的模式是指数退避重试。

数据验证错误

错误的服务器响应格式、缺少必填字段、错误的数据类型——解析错误 如果应用程序正确处理错误数据,则是非致命的。典型的方法是使用默认后备值,并记录带有请求上下文的解析错误,以便稍后在服务器上进行分析。

UI 渲染错误

图片加载问题、错误的字体、布局错误——这些都不是致命的,但会降低用户体验。占位图片 和后备值可以避免空白屏幕,并使错误不那么明显。在 React Native 中,UI 错误使用 Error Boundary 并显示备用组件。

业务逻辑和状态错误

计算错误、状态不一致、屏幕之间的错误转换——逻辑错误 通常不会导致崩溃,但会导致应用程序行为不正确。如果没有系统性的记录和监控,它们更难被发现,因为它们不会生成崩溃报告,并且在用户投诉之前一直不被注意。

Non-Fatal Error 与 Fatal Error:对比

Non-Fatal Error 与 fatal 的不同之处在于它给程序留下继续工作的可能性。Fatal error 是一种应用程序无法恢复的状态:空指针解引用、堆栈溢出、内存不足。Non-fatal 错误可以被捕获、处理并继续执行,而 fatal error 则需要重新启动应用程序。

特征Non-Fatal ErrorFatal Error
应用程序终止
恢复可能性是,通过 catch 块
记录通过 recordException 从代码中仅由崩溃报告器
对 UX 的影响暂时不便会话完全丢失
示例Network timeout, parse errorNullPointerException, OOM

non-fatal 和 fatal 之间的界限可能取决于实现。网络超时 在一个应用程序中作为 non-fatal 处理(1-2 秒后重试请求),在另一个应用程序中可能是致命的(没有处理器时崩溃)。高质量的错误处理将潜在致命的情况转化为非致命的情况,从而提高应用程序的稳定性。设计错误处理系统是开发高可靠性移动应用程序时关键的架构任务之一。内置的监控系统允许团队在非致命错误影响大量用户之前快速发现并修复它们。

记录 non-fatal 错误

Firebase Crashlytics — 是移动应用程序中记录非致命错误的主要工具。recordException 方法允许在不停应用程序运行的情况下,记录带有完整堆栈跟踪和执行上下文的 non-fatal 异常。与崩溃报告不同,recordException 可以在代码的任何位置调用以记录捕获的异常。

kotlin
fun fetchUserData(userId: String) {
    try {
        val response = apiService.getUser(userId)
        updateUI(response)
    } catch (e: IOException) {
        Crashlytics.log("Network error for user $userId")
        Crashlytics.recordException(e)
        showRetryDialog()
    } catch (e: JsonParseException) {
        // Non-fatal:使用备用数据
        Crashlytics.recordException(e)
        showFallbackContent()
    }
}

// 使用用户密钥进行记录
Crashlytics.setCustomKey("screen", "Profile")
Crashlytics.setCustomKey("api_version", "v3")

Sentry — 是 Crashlytics 的替代方案,具有更详细的 non-fatal 错误诊断功能。Sentry SDK 提供了 captureException 方法,用于将异常详细信息发送到服务器。Sentry 的主要优势是将类似的 non-fatal 错误分组到同一个 issue 中,分析重复频率以及以面包屑形式提供的执行上下文——错误之前用户操作的顺序。

记录 non-fatal 错误的标准

并非所有 non-fatal 错误都需要记录。预期状态 — 没有连接时的网络故障——可以选择性地记录。意外错误 — 处理代码中的 NullPointerException、错误的数据格式、logic error——应该始终记录。每个团队根据应用程序的背景确定重要性阈值:平均每 1000 名用户每天 10 到 20 个独特的 non-fatal 错误被认为是正常的。重要的是设置关于 non-fatal 错误数量急剧增加的警报——这可能表明新版本 API 存在问题或发布后出现回归。

在代码中处理 non-fatal 错误

基本的处理机制是 try-catch,它捕获异常并执行恢复代码。对于网络操作,典型的模式是指数退避重试。对于解析错误——使用默认后备值并记录上下文以便在服务器端进行分析。

swift
func loadImage(from url: URL) -> UIImage? {
    do {
        let data = try Data(contentsOf: url)
        return UIImage(data: data)
    } catch {
        Logger.shared.logError(error: "Image load failed: \(url)")
        return UIImage(named: "placeholder")
    }
}

func performRequest() async throws -> Data {
    var lastError: Error? = nil
    for attempt in 0..<3 {
        do {
            return try await URLSession.shared.data(from: url)
        } catch {
            lastError = error
            try await Task.sleep(UInt64(pow(2, attempt)) * 1_000_000_000)
        }
    }
    throw lastError ?? URLError(.unknown)
}

Result 类型 — 是一种无需异常的替代方法。函数返回带有 Success 和 Failure 变体的密封类 Result。调用代码显式处理两个变体,从而消除了未处理的错误。Result 类型在 Kotlin(标准库中的 Result)和 Swift(Result)中流行,用于在类型层面上显式处理 non-fatal 状态。

Non-fatal 错误的后备策略

对于每种类型的 non-fatal 错误,都应该预见到 恢复策略:网络错误时加载缓存数据,解析错误时使用默认值,UI 错误时重新初始化组件。好的做法是向用户显示包含错误消息的 toast 或 snackbar,但不要完全阻止与应用程序的交互。重要的是区分可恢复(recoverable)和不可恢复的错误——对于后者,恢复策略将不同,例如建议重新启动屏幕或清除数据。缓存先前成功状态通常是移动平台上处理 non-fatal 错误最简单和最有效的方法。

常见问题

Non-fatal 错误与 warning 有何不同?

Warning — 是编译器或静态分析器关于代码中潜在问题的警告。Non-fatal error — 是已经发生但未导致崩溃的运行时异常。Warning 可以在编译前消除,non-fatal error — 可以在执行期间通过 catch 块处理。

是否应该记录所有 non-fatal 错误?

不,过多的记录会污染监控。应该记录 意外错误 在生产环境中,并忽略预期的状态:没有连接时的网络故障选择性记录,而处理代码中的 NullPointerException — 始终记录。每个团队根据应用程序的背景确定重要性阈值。

如何在 SwiftUI 中处理 non-fatal 错误?

在 SwiftUI 中使用带有 @Published errorState 字段的 ObservableObject 来跟踪错误状态。视图订阅更改并显示替代内容。在 iOS 17 之前使用 Combine 及处理器,从 iOS 17 开始使用 SwiftData 和 @Observable 宏进行响应式 UI 更新。

Non-fatal 错误会变成 fatal 吗?

是的,如果错误引起连锁反应。示例:图像加载的非致命故障可能导致 UI 状态错误,进而在尝试显示时导致崩溃。在每个级别上高质量地处理 non-fatal 错误可以防止它们升级到致命级别。

Non-fatal 在 iOS 和 Android 中有什么不同?

在 iOS 中,non-fatal 错误通过 do-catch 和 throw 处理,在 Android 中——通过 try-catch 和异常。iOS 使用带有域和错误代码的 NSError,Android 使用 Java/Kotlin 异常。Crashlytics 在两个平台上通过 recordException 以相同方式工作,提供统一的监控接口。

总结

  • Non-Fatal Error — 不终止应用程序且允许恢复执行的运行时错误
  • 网络错误、解析错误和 UI 渲染错误 — 非致命错误的三个主要类别
  • Fatal Error — non-fatal 的对立面,导致应用程序完全崩溃且无法恢复
  • Crashlytics 和 Sentry — 生产环境中记录 non-fatal 错误的主要工具
  • Result 类型 — 异常替代方案,用于在类型层面上显式处理错误状态
  • 占位值 和后备策略防止用户体验的明显恶化
  • 系统性地修复 non-fatal 错误可根据 Instabug 提高留存率和应用程序质量

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读