移动开发中的错误处理:概念、技术及如何组织

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

错误处理是移动开发人员的基本技能。根据HackerOne(2025)的数据,62%的数据泄露是由于未处理的异常造成的。正确的错误处理不仅可以防止崩溃,还可以保护用户数据。让我们来看看iOS、Android和React Native的方法。

关键要点

  • iOS使用do-catch、throw、guard let和if-let进行错误处理。Swift在语言级别不允许未处理的异常。
  • Android/Kotlin提供try-catch、elvis操作符、sealed class和Result类型。Sealed class是建模错误状态的强大工具。
  • Kotlin Result和函数库中的Either强制在编译时处理错误,使代码更可靠。
  • Crash Reporting(Crashlytics、Sentry)是生产环境的必备工具。没有它,你只能从用户那里了解错误。
  • React Native中的Error Boundary可防止JavaScript错误导致应用完全崩溃。将其用于根组件

iOS中的错误处理:Do-Catch、Throw、Guard Let

错误处理在Swift中基于四个关键机制:do-catch、throws、guard let和if-let。与许多语言不同,Swift不允许未捕获的异常——每个错误都必须显式处理或通过throws声明。错误处理是移动开发的关键技能,直接影响应用程序的稳定性。

Do-Catch和Throw

do-catch是调用标记为throws的函数的标准块。在do内部,函数通过try被调用,如果抛出错误,控制权转移到catch。可以通过模式匹配处理不同类型的错误。如果错误未处理,它会沿堆栈向上传播(Error Propagation)。要在iOS中有效处理错误,请使用do-catch作为主要机制。

Throw在函数签名中声明:func fetchData() throws -> Data。这意味着调用代码必须通过try、try?或try!处理错误。try?将错误转换为nil,try!在错误时导致崩溃(仅在确定成功时使用)。通过throw进行错误处理是Swift中的强制性实践。

Optional/Nullable和Guard Let

Guard let是一种在值为nil时提前退出函数的构造。与if-let不同,guard let需要在else分支中退出(return、throw、break)。这使得代码更扁平、更易读——没有嵌套的if块。如果optional不可能为nil——使用force unwrap(!),只有在绝对确定时。在移动应用中,guard let有助于在处理可选值时避免崩溃。

Optional Chaining(user?.address?.city)和nil-coalescing(??)是在不解包的情况下处理optional的语法糖。在IT Sectr,我们使用guard let验证API输入参数,并要求团队避免在没有明确注释的情况下使用force unwrap。每层的错误处理器都能防止意外故障。

Android中的错误处理:Try-Catch、Elvis、Sealed Class

Kotlin是Android开发的主要语言。它继承自Java的try-catch,但添加了更安全的替代方案:elvis操作符、require、check和sealed class。Kotlin中的错误处理基于这些机制的组合。与Swift不同,Kotlin不需要处理受检异常(所有异常都是未受检的)。对于Android上的移动应用程序中的错误处理,请使用sealed class作为主要模式。

Try-Catch和Elvis操作符

Try-catch在Kotlin中作为表达式工作——它返回一个值。val result = try { fetchData() } catch (e: Exception) { fallbackValue }。这缩短了代码。Elvis操作符(?:)类似于nullable类型的nil-coalescing:val name = user?.name ?: "Guest"。对于移动应用程序中的错误处理,try-catch作为表达式是最简洁的方法。

Sealed class是建模成功和错误状态的强大工具。sealed class NetworkResult { data class Success(val data: T) : NetworkResult(); data class Error(val message: String) : NetworkResult() }。在when表达式中使用时,编译器检查分支的完整性。通过sealed class进行错误处理可确保没有状态被遗漏。

kotlin
// Sealed class + try-catch — Android的典型模式
sealed class NetworkResult<out T> {
    data class Success<out T>(val data: T) : NetworkResult<T>()
    data class Error(val message: String) : NetworkResult<Nothing>()
}

fun fetchUser(id: String): NetworkResult<User> {
    return try {
        NetworkResult.Success(api.getUser(id))
    } catch (e: Exception) {
        NetworkResult.Error("Failed: ${e.message}")
    }
}

在示例中,sealed class NetworkResult建模了两个状态:带数据的成功和带消息的错误。fetchUser函数在任何情况下都返回结果,调用代码通过when处理两个分支。这消除了未处理错误的可能性。通过sealed class进行错误处理是IT Sectr中Android开发的标准。

Kotlin中的错误处理:Result和Either

Result是Kotlin内置的类型,用于表示可能失败的操作的结果。它强制通过fold、getOrThrow或map处理成功和失败。Result在异步链(协程)中很有用。使用Result进行错误处理是Kotlin移动开发的标准。

Result与Either

Either是Arrow库中的函数式类型,允许返回两种类型之一的值(Left — 错误,Right — 成功)。与Result不同,Either可以包含任何用户定义的错误类型。对于简单的项目,内置的Result就足够了;对于复杂的项目,请使用Arrow的Either。错误处理工具的选择取决于项目的复杂性。

错误传播

错误传播是一种错误沿调用堆栈向上传播直到被处理的机制。在Kotlin中,这是默认发生的(未受检异常)。在Swift中,这仅适用于标记为throws的函数。对于Result和Either,错误不会传播——它们保留在类型中,你必须处理它们。这使得移动应用程序中的错误处理更加安全。

参数 iOS(Swift) Android(Kotlin)
基本机制do-catch + throwstry-catch(表达式)
Optional/Nullableguard let、if-let、???. let、elvis(?:)
函数式方法Result(Swift 5+)Result、Either(Arrow)
错误建模Enum:ErrorSealed class
受检异常是(throws)否(全部未受检)
非致命os_log、CrashlyticsTimber、Crashlytics

表格显示了关键差异。iOS需要显式的错误声明(throws),使代码更安全但更冗长。Android依赖于开发者的自律。在IT Sectr,我们对Android使用sealed class,对iOS使用throws——这是两个平台在移动应用程序中错误处理的最佳实践。

崩溃报告:Crashlytics和Sentry

崩溃报告是收集和分析应用程序崩溃的系统。崩溃报告是生产环境中错误处理的重要组成部分。没有它,你只能从用户那里了解问题,这对于生产环境是不可接受的。两个主要工具:Firebase Crashlytics(免费)和Sentry(基本使用免费)。对于移动应用程序中的错误处理,请从第一个版本就开始实施崩溃报告。

Firebase Crashlytics

Crashlytics是Firebase的一部分。它自动收集崩溃,按调用堆栈分组,并显示受影响用户的数量。它支持通过recordException()记录非致命错误。集成:将SDK添加到build.gradle(Android)或Podfile(iOS)。Crashlytics是项目开始时进行错误处理的最佳免费工具。

Sentry

Sentry是一个跨平台错误监控系统。与Crashlytics不同,Sentry提供详细的跟踪(breadcrumbs)、性能监控和React Native支持。它允许查看错误发生时的应用程序状态。IT Sectr建议需要完全控制移动开发中错误处理的项目使用Sentry。

React Native中的Error Boundary

Error Boundary是一个React组件,它捕获子组件树中的JavaScript错误并显示备用UI,防止应用程序完全崩溃。Error Boundary是React Native中错误处理的关键组件。对关键屏幕和导航使用error boundaries。React Native中移动应用程序的错误处理需要在顶层正确设置Error Boundary。

Error Boundary实现

Error Boundary通过componentDidCatch(error,errorInfo)或static getDerivedStateFromError(error)创建。它不捕获异步代码(setTimeout、requestAnimationFrame)、服务器端渲染或原生错误(Native Modules)中的错误。对于日志记录,在componentDidCatch内部使用崩溃报告SDK。Error Boundary是UI层的一个简单但有效的错误处理器。

致命错误与非致命错误

致命错误是导致应用程序崩溃的未处理异常。非致命错误是您捕获并处理了的异常,但它表明代码中存在错误。非致命错误通过Crashlytics/Sentry记录,有助于在错误变得致命之前发现错误。致命错误和非致命错误都需要在移动开发中进行正确的错误处理。

常见问题

Kotlin中try-catch和Result有什么区别?

try-catch是用于异常的语言机制。Result是一种包装类型,强制在编译时处理错误。在IT Sectr,我们更倾向于在业务逻辑中使用Result,在与外部系统协作时使用try-catch。这两种方法都是Kotlin中通用错误处理的一部分。

React Native中的Error Boundary是什么?

Error Boundary是一个React组件,它捕获子组件树中的JavaScript错误并显示备用UI,而不是使整个应用程序崩溃。它不捕获异步代码或服务器端渲染中的错误。Error Boundary是React Native移动应用程序中错误处理的重要元素。

新项目应该使用Crashlytics还是Sentry?

Crashlytics(Firebase)是开始的最佳选择:免费、集成简单、自动崩溃分组。Sentry用于需要详细错误跟踪和性能监控的项目。错误处理工具的选择取决于预算和监控要求。

什么是非致命错误,它与致命错误有何不同?

致命错误是应用程序崩溃(未捕获的异常)。非致命错误是您捕获并处理了的异常,但它表明代码中存在错误。非致命错误单独记录,有助于在错误变得致命之前发现错误。移动应用程序中的错误处理应包括对两种类型的监控。

什么时候应该在Swift中使用guard let而不是if-let?

guard let用于在值缺失时提前退出函数——这使代码更线性、更易读。当块内需要optional且无需退出函数时,if-let是合适的。guard let更适合验证输入参数,并且是iOS中错误处理的一部分。

总结

  • iOS使用do-catch、throws和guard let——每个错误都必须在函数签名中声明。iOS中的错误处理需要显式声明。
  • Android/Kotlin提供作为表达式的try-catch、elvis操作符和用于错误建模的sealed class。Android中的错误处理更灵活,但需要自律。
  • Sealed class和Result是Kotlin中函数式错误处理的最佳实践。它们消除了未处理的状态。
  • 崩溃报告(Crashlytics、Sentry)对生产环境是强制性的。从Crashlytics开始,随着项目增长切换到Sentry。没有监控,移动应用程序中的错误处理是不可能的。
  • React Native中的Error Boundary可防止UI完全崩溃。在导航的最高级别使用。
  • 非致命错误与致命错误同等重要——它们在应用程序崩溃前指示问题。错误处理器应记录这两种类型。
  • Global Exception Handler是最后一道防线。实现Thread.setDefaultUncaughtExceptionHandler(Android)或NSSetUncaughtExceptionHandler(iOS)以记录所有未捕获的错误。

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

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

讨论项目