Xử lý lỗi trong phát triển di động: khái niệm, kỹ thuật và cách tổ chức

Tác giả: IT Sectr Đã đăng: 2026-05-23 Thời gian đọc: 11 phút

Xử lý lỗi là kỹ năng cơ bản của nhà phát triển di động. Theo HackerOne (2025), 62% vụ rò rỉ dữ liệu xảy ra do các ngoại lệ không được xử lý. Xử lý lỗi đúng cách không chỉ ngăn chặn sự cố mà còn bảo vệ dữ liệu người dùng. Hãy cùng tìm hiểu các phương pháp cho iOS, Android và React Native.

Những điểm chính

  • iOS sử dụng do-catch, throw, guard let và if-let để xử lý lỗi. Swift không cho phép ngoại lệ chưa được xử lý ở cấp độ ngôn ngữ.
  • Android/Kotlin cung cấp try-catch, toán tử elvis, sealed class và kiểu Result. Sealed class là công cụ mạnh mẽ để mô hình hóa trạng thái lỗi.
  • Kotlin Result và Either từ các thư viện hàm buộc xử lý lỗi tại thời điểm biên dịch, làm cho mã đáng tin cậy hơn.
  • Crash Reporting (Crashlytics, Sentry) là công cụ bắt buộc cho môi trường sản xuất. Nếu không có nó, bạn chỉ biết về lỗi từ người dùng.
  • Error Boundary trong React Native ngăn chặn sự cố toàn bộ ứng dụng do lỗi JavaScript. Sử dụng nó cho các thành phần gốc.

Xử lý lỗi trong iOS: Do-Catch, Throw, Guard Let

Xử lý lỗi trong Swift được xây dựng trên bốn cơ chế chính: do-catch, throws, guard let và if-let. Không giống như nhiều ngôn ngữ, Swift không cho phép ngoại lệ không được bắt — mọi lỗi phải được xử lý rõ ràng hoặc khai báo thông qua throws. Xử lý lỗi là kỹ năng quan trọng cho phát triển di động, ảnh hưởng trực tiếp đến độ ổn định của ứng dụng.

Do-Catch và Throw

do-catch là khối tiêu chuẩn để gọi các hàm được đánh dấu bằng throws. Bên trong do, một hàm được gọi với try, và nếu nó ném ra lỗi, quyền điều khiển chuyển sang catch. Có thể xử lý các loại lỗi khác nhau thông qua pattern matching. Nếu lỗi không được xử lý, nó sẽ lan truyền lên trên ngăn xếp (Error Propagation). Để xử lý lỗi hiệu quả trong iOS, hãy sử dụng do-catch làm cơ chế chính.

Throw được khai báo trong chữ ký hàm: func fetchData() throws -> Data. Điều này có nghĩa là mã gọi phải xử lý lỗi thông qua try, try? hoặc try!. try? chuyển đổi lỗi thành nil, try! gây ra sự cố khi có lỗi (chỉ sử dụng nếu bạn chắc chắn thành công). Xử lý lỗi thông qua throw là thực hành bắt buộc trong Swift.

Optional/Nullable và Guard Let

Guard let là cấu trúc để thoát sớm khỏi hàm nếu giá trị là nil. Không giống như if-let, guard let yêu cầu một lối thoát (return, throw, break) trong nhánh else. Điều này làm cho mã phẳng hơn và dễ đọc hơn — không có khối if lồng nhau. Nếu optional không thể là nil — hãy sử dụng force unwrap (!), chỉ khi hoàn toàn chắc chắn. Trong ứng dụng di động, guard let giúp tránh sự cố khi xử lý các giá trị tùy chọn.

Optional Chaining (user?.address?.city) và nil-coalescing (??) là đường cú pháp để làm việc với optional mà không cần mở gói. Tại IT Sectr, chúng tôi sử dụng guard let để xác thực tham số đầu vào API và yêu cầu nhóm tránh force unwrap mà không có bình luận rõ ràng. Trình xử lý lỗi ở mọi cấp độ bảo vệ khỏi các lỗi không mong đợi.

Xử lý lỗi trong Android: Try-Catch, Elvis, Sealed Class

Kotlin là ngôn ngữ chính cho phát triển Android. Nó kế thừa try-catch từ Java nhưng bổ sung các lựa chọn thay thế an toàn hơn: toán tử elvis, require, check và sealed class. Xử lý lỗi trong Kotlin được xây dựng trên sự kết hợp của các cơ chế này. Không giống như Swift, Kotlin không yêu cầu xử lý các ngoại lệ đã kiểm tra (tất cả ngoại lệ đều không được kiểm tra). Để xử lý lỗi trong ứng dụng di động trên Android, hãy sử dụng sealed class làm mẫu chính.

Try-Catch và Toán tử Elvis

Try-catch trong Kotlin hoạt động như một biểu thức — nó trả về một giá trị. val result = try { fetchData() } catch (e: Exception) { fallbackValue }. Điều này làm giảm mã. Toán tử Elvis (?:) tương tự như nil-coalescing cho kiểu nullable: val name = user?.name ?: "Guest". Để xử lý lỗi trong ứng dụng di động, try-catch như một biểu thức là cách tiếp cận ngắn gọn nhất.

Sealed class là công cụ mạnh mẽ để mô hình hóa trạng thái thành công và lỗi. sealed class NetworkResult { data class Success(val data: T) : NetworkResult(); data class Error(val message: String) : NetworkResult() }. Khi được sử dụng trong biểu thức when, trình biên dịch kiểm tra tính đầy đủ của các nhánh. Xử lý lỗi thông qua sealed class đảm bảo không có trạng thái nào bị bỏ qua.

kotlin
// Sealed class + try-catch — mẫu điển hình cho 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}")
    }
}

Trong ví dụ, sealed class NetworkResult mô hình hóa hai trạng thái: thành công với dữ liệu và lỗi với thông báo. Hàm fetchUser trả về kết quả trong mọi trường hợp và mã gọi xử lý cả hai nhánh thông qua when. Điều này loại bỏ khả năng lỗi không được xử lý. Xử lý lỗi thông qua sealed class là tiêu chuẩn cho phát triển Android tại IT Sectr.

Xử lý lỗi trong Kotlin: Result và Either

Result là kiểu tích hợp của Kotlin để biểu diễn kết quả của một hoạt động có thể thất bại. Nó buộc phải xử lý thành công và thất bại thông qua fold, getOrThrow hoặc map. Result hữu ích trong các chuỗi bất đồng bộ (coroutines). Xử lý lỗi với Result là tiêu chuẩn cho phát triển di động trong Kotlin.

Result vs Either

Either là kiểu hàm từ thư viện Arrow cho phép trả về giá trị của một trong hai kiểu (Left — lỗi, Right — thành công). Không giống như Result, Either có thể chứa bất kỳ kiểu lỗi nào do người dùng định nghĩa. Đối với dự án đơn giản, Result tích hợp là đủ; đối với dự án phức tạp, hãy sử dụng Either từ Arrow. Việc chọn công cụ xử lý lỗi phụ thuộc vào độ phức tạp của dự án.

Lan truyền lỗi

Lan truyền lỗi là cơ chế mà lỗi lan truyền lên trên ngăn xếp cuộc gọi cho đến khi được xử lý. Trong Kotlin, điều này xảy ra theo mặc định (ngoại lệ unchecked). Trong Swift, điều này chỉ áp dụng cho các hàm được đánh dấu bằng throws. Với Result và Either, lỗi không lan truyền — chúng ở trong kiểu và bạn phải xử lý chúng. Điều này làm cho việc xử lý lỗi trong ứng dụng di động an toàn hơn.

Tham số iOS (Swift) Android (Kotlin)
Cơ chế cơ bảndo-catch + throwstry-catch (expression)
Optional/Nullableguard let, if-let, ???. let, elvis (?:)
Phương pháp hàmResult (Swift 5+)Result, Either (Arrow)
Mô hình hóa lỗiEnum: ErrorSealed class
Ngoại lệ đã kiểm traCó (throws)Không (tất cả unchecked)
Non-fatalos_log, CrashlyticsTimber, Crashlytics

Bảng cho thấy sự khác biệt chính. iOS yêu cầu khai báo lỗi rõ ràng (throws), làm cho mã an toàn hơn nhưng dài dòng hơn. Android phụ thuộc vào kỷ luật của nhà phát triển. Tại IT Sectr, chúng tôi sử dụng sealed class cho Android và throws cho iOS — đây là thực hành tốt nhất của cả hai nền tảng để xử lý lỗi trong ứng dụng di động.

Báo cáo sự cố: Crashlytics và Sentry

Báo cáo sự cố là hệ thống thu thập và phân tích sự cố ứng dụng. Báo cáo sự cố là phần thiết yếu của xử lý lỗi trong môi trường sản xuất. Nếu không có nó, bạn biết về vấn đề từ người dùng, điều không thể chấp nhận cho sản xuất. Hai công cụ chính: Firebase Crashlytics (miễn phí) và Sentry (miễn phí cho sử dụng cơ bản). Để xử lý lỗi trong ứng dụng di động, hãy luôn triển khai báo cáo sự cố từ bản phát hành đầu tiên.

Firebase Crashlytics

Crashlytics là một phần của Firebase. Nó tự động thu thập sự cố, nhóm chúng theo ngăn xếp cuộc gọi và hiển thị số lượng người dùng bị ảnh hưởng. Nó hỗ trợ ghi nhật ký lỗi không nghiêm trọng thông qua recordException(). Tích hợp: thêm SDK vào build.gradle (Android) hoặc Podfile (iOS). Crashlytics là công cụ miễn phí tốt nhất để xử lý lỗi khi bắt đầu dự án.

Sentry

Sentry là hệ thống giám sát lỗi đa nền tảng. Không giống như Crashlytics, Sentry cung cấp theo dõi chi tiết (breadcrumbs), giám sát hiệu suất và hỗ trợ React Native. Nó cho phép xem trạng thái ứng dụng tại thời điểm xảy ra lỗi. IT Sectr khuyên dùng Sentry cho các dự án cần kiểm soát hoàn toàn việc xử lý lỗi trong phát triển di động.

Error Boundary trong React Native

Error Boundary là thành phần React bắt lỗi JavaScript trong cây thành phần con và hiển thị giao diện dự phòng, ngăn chặn sự cố toàn bộ ứng dụng. Error Boundary là thành phần chính để xử lý lỗi trong React Native. Sử dụng error boundaries cho các màn hình quan trọng và điều hướng. Xử lý lỗi trong ứng dụng di động trên React Native yêu cầu thiết lập Error Boundary thích hợp ở cấp cao nhất.

Triển khai Error Boundary

Error Boundary được tạo thông qua componentDidCatch(error, errorInfo) hoặc static getDerivedStateFromError(error). Nó không bắt lỗi trong mã bất đồng bộ (setTimeout, requestAnimationFrame), kết xuất phía máy chủ hoặc lỗi gốc (Native Modules). Để ghi nhật ký, hãy sử dụng SDK báo cáo sự cố bên trong componentDidCatch. Error Boundary là trình xử lý lỗi đơn giản nhưng hiệu quả cho lớp giao diện.

Lỗi nghiêm trọng và không nghiêm trọng

Lỗi nghiêm trọng là ngoại lệ không được xử lý gây ra sự cố ứng dụng. Lỗi không nghiêm trọng là ngoại lệ bạn đã bắt và xử lý, nhưng nó chỉ ra vấn đề trong mã. Lỗi không nghiêm trọng được ghi nhật ký qua Crashlytics/Sentry và giúp tìm lỗi trước khi chúng trở nên nghiêm trọng. Cả lỗi nghiêm trọng và không nghiêm trọng đều yêu cầu xử lý lỗi thích hợp trong phát triển di động.

Câu hỏi thường gặp

Sự khác biệt giữa try-catch và Result trong Kotlin là gì?

try-catch là cơ chế ngôn ngữ cho ngoại lệ. Result là kiểu bao bọc buộc xử lý lỗi tại thời điểm biên dịch. Tại IT Sectr, chúng tôi ưu tiên Result cho logic kinh doanh và try-catch cho làm việc với hệ thống bên ngoài. Cả hai phương pháp đều là một phần của xử lý lỗi chung trong Kotlin.

Error Boundary trong React Native là gì?

Error Boundary là thành phần React bắt lỗi JavaScript trong cây thành phần con và hiển thị giao diện dự phòng thay vì làm sập toàn bộ ứng dụng. Nó không bắt lỗi trong mã bất đồng bộ hoặc kết xuất phía máy chủ. Error Boundary là yếu tố quan trọng của xử lý lỗi trong ứng dụng di động trên React Native.

Tôi nên sử dụng Crashlytics hay Sentry cho dự án mới?

Crashlytics (Firebase) là lựa chọn tốt nhất để bắt đầu: miễn phí, tích hợp đơn giản, nhóm sự cố tự động. Sentry dành cho các dự án cần theo dõi lỗi chi tiết và giám sát hiệu suất. Việc chọn công cụ xử lý lỗi phụ thuộc vào ngân sách và yêu cầu giám sát.

Lỗi không nghiêm trọng là gì và khác gì với lỗi nghiêm trọng?

Lỗi nghiêm trọng là sự cố ứng dụng (ngoại lệ không được bắt). Lỗi không nghiêm trọng là ngoại lệ bạn đã bắt và xử lý, nhưng nó chỉ ra vấn đề trong mã. Lỗi không nghiêm trọng được ghi nhật ký riêng và giúp tìm lỗi trước khi chúng trở nên nghiêm trọng. Xử lý lỗi trong ứng dụng di động nên bao gồm giám sát cả hai loại.

Khi nào nên sử dụng guard let thay vì if-let trong Swift?

guard let được sử dụng để thoát sớm khỏi hàm khi thiếu giá trị — điều này làm cho mã tuyến tính và dễ đọc hơn. if-let phù hợp khi cần optional trong một khối và không cần thoát khỏi hàm. guard let được ưu tiên để xác thực tham số đầu vào và là một phần của xử lý lỗi trong iOS.

Tóm tắt

  • iOS sử dụng do-catch, throws và guard let — mọi lỗi phải được khai báo trong chữ ký hàm. Xử lý lỗi trong iOS yêu cầu khai báo rõ ràng.
  • Android/Kotlin cung cấp try-catch như biểu thức, toán tử elvis và sealed class để mô hình hóa lỗi. Xử lý lỗi trong Android linh hoạt hơn nhưng cần kỷ luật.
  • Sealed class và Result là thực hành tốt nhất cho xử lý lỗi hàm trong Kotlin. Chúng loại bỏ các trạng thái không được xử lý.
  • Báo cáo sự cố (Crashlytics, Sentry) là bắt buộc cho sản xuất. Bắt đầu với Crashlytics, chuyển sang Sentry khi dự án phát triển. Xử lý lỗi trong ứng dụng di động là không thể nếu không có giám sát.
  • Error Boundary trong React Native ngăn chặn sự cố toàn bộ giao diện. Sử dụng ở cấp điều hướng cao nhất.
  • Lỗi không nghiêm trọng cũng quan trọng như lỗi nghiêm trọng — chúng chỉ ra vấn đề trước khi ứng dụng sự cố. Trình xử lý lỗi nên ghi nhật ký cả hai loại.
  • Global Exception Handler là tuyến phòng thủ cuối cùng. Triển khai Thread.setDefaultUncaughtExceptionHandler (Android) hoặc NSSetUncaughtExceptionHandler (iOS) để ghi nhật ký tất cả lỗi không được bắt.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án