Crash trong phát triển di động: định nghĩa, các loại và phương pháp ngăn chặn

Tác giả: IT Sectr Đã đăng: 2026-03-29 Thời gian đọc: 9 phút

Crash là sự kết thúc bất thường của một ứng dụng di động do một ngoại lệ không được xử lý hoặc lỗi hệ thống nghiêm trọng. Theo Firebase Crashlytics, khoảng 2% người dùng gặp sự cố hàng ngày và mỗi lần sập giảm tỷ lệ giữ chân người dùng 10–20%. Hiểu nguyên nhân và phương pháp ngăn chặn sự cố là kỹ năng thiết yếu cho bất kỳ nhà phát triển di động nào.

Các điểm chính

  • Crash — một ngoại lệ không được xử lý dẫn đến kết thúc quá trình bất thường
  • NullPointerException — loại sập phổ biến nhất trong ứng dụng Java/Kotlin
  • Trình báo cáo sự cố thu thập stack trace, trạng thái thiết bị và dữ liệu người dùng
  • Firebase Crashlytics — công cụ tiêu chuẩn để giám sát sự cố trong phát triển di động
  • Ngăn chặn bao gồm xử lý lỗi đúng cách, kiểm thử và kiểm tra an toàn null

Crash là gì

Crash là sự kết thúc bất thường của một ứng dụng do một ngoại lệ không được xử lý hoặc tín hiệu hệ thống nghiêm trọng không được xử lý trong mã ứng dụng. Khi hệ thống hoặc máy ảo (JVM, ART) phát hiện tình trạng nghiêm trọng — NullPointerException, IndexOutOfBoundsException, OutOfMemoryError — nó ngay lập tức dừng tiến trình và giải phóng khỏi bộ nhớ. Người dùng thấy ứng dụng đột ngột đóng mà không có thông báo lỗi hệ thống nào. Theo Google, các ứng dụng có tỷ lệ không sập dưới 99% mất tới 20% người dùng hoạt động hàng tháng.

Trên Android, cơ chế xử lý sự cố khác với hệ thống desktop. Thay vì hộp thoại gỡ lỗi với stack trace, Android chỉ đơn giản là tiêu diệt tiến trình mà không lưu thông tin chi tiết. Việc thu thập thông tin sự cố là nhiệm vụ của các thư viện bên thứ ba (Crashlytics, Sentry, Bugsnag) chặn các ngoại lệ thông qua Thread.setDefaultUncaughtExceptionHandler trước khi tiến trình bị kết thúc.

iOS sử dụng cơ chế tương tự với NSException và Mach exceptions để xử lý lỗi nghiêm trọng. Khi một ngoại lệ không được xử lý xảy ra, hệ thống kết thúc ứng dụng và báo cáo được lưu dưới dạng tệp .crash. Việc thu thập sự cố trên iOS yêu cầu tích hợp với Crashlytics hoặc báo cáo tích hợp thông qua Xcode Organizer.

Các loại sự cố chính

Năm danh mục sự cố bao phủ 90% tất cả các lỗi trong ứng dụng di động. Hiểu từng loại giúp chẩn đoán và sửa lỗi nhanh hơn trong môi trường sản xuất.

NullPointerException — vua của các sự cố

NullPointerException (NPE) là loại sự cố phổ biến nhất trong tất cả ứng dụng Java/Kotlin. Nó xảy ra khi cố gắng gọi phương thức hoặc truy cập trường của một đối tượng có giá trị null. Các kịch bản điển hình: trường Activity chưa được khởi tạo khi xoay màn hình, phản hồi null từ máy chủ khi giải tuần tự hóa JSON, điều hướng bất cẩn qua bộ điều hợp RecyclerView.

Kotlin giải quyết vấn đề NPE ở cấp độ ngôn ngữ thông qua kiểu an toàn null: String? không thể được sử dụng mà không có kiểm tra rõ ràng. Tuy nhiên, khả năng tương thích với Java và Reflection vẫn tạo ra rủi ro. Sử dụng chú thích @NonNull và @Nullable và bật strictNullChecks trong các công cụ phân tích tĩnh.

kotlin
fun safeLength(text: String?): Int {
    return text?.length ?: 0 // xử lý null an toàn
}

IndexOutOfBoundsException và lỗi bộ sưu tập

IndexOutOfBoundsException xảy ra khi truy cập vào chỉ mục không tồn tại của danh sách hoặc mảng. Các kịch bản phổ biến: xóa phần tử khỏi RecyclerView mà không đồng bộ hóa với bộ điều hợp, sửa đổi ArrayList đa luồng mà không khóa, tính toán sai vị trí trong ViewPager. ConcurrentModificationException là họ hàng gần khi lặp và sửa đổi bộ sưu tập đồng thời.

Sử dụng CopyOnWriteArrayList để truy cập đa luồng hoặc bộ sưu tập không khóa từ java.util.concurrent. Để đồng bộ hóa UI, sử dụng DiffUtil để tính toán sự khác biệt giữa danh sách cũ và mới một cách an toàn và hiệu quả.

ClassCastException — vấn đề về kiểu

ClassCastException xảy ra khi ép kiểu đối tượng sang kiểu không tương thích. Trong Android, nguyên nhân điển hình: sai kiểu ViewHolder trong RecyclerView (các loại ô khác nhau không có getItemViewType phù hợp), ép kiểu Fragment sai khi điều hướng, đối tượng Serializable với các phiên bản lớp khác nhau.

Sử dụng ép kiểu an toàn của Kotlin qua toán tử as?, trả về null khi không tương thích kiểu. Trong Java — kiểm tra qua instanceof trước khi ép kiểu. Đối với đối tượng Parcelable, luôn khai báo CREATOR trong mỗi lớp.

IllegalStateException và lỗi logic

IllegalStateException báo hiệu việc gọi phương thức ở trạng thái không phù hợp của đối tượng. Ví dụ điển hình trong Android — getSupportFragmentManager() sau onSaveInstanceState, khi commit() của fragment không được phép. Một trường hợp phổ biến khác — gọi dismiss() trên hộp thoại đã đóng.

Kiểm tra trạng thái vòng đời trước các thao tác với FragmentManager. Chỉ sử dụng commitAllowingStateLoss() khi bạn chắc chắn rằng việc mất trạng thái không quan trọng. Trong Kotlin, tạo các builder giống DSL loại bỏ trạng thái không hợp lệ ở cấp độ kiểu.

Native Crash (tín hiệu SIGSEGV, SIGABRT)

Native Crash xảy ra trong mã C/C++ gốc do vi phạm bộ nhớ: giải tham chiếu con trỏ null, double-free, tràn bộ đệm stack. Trên Android, các sự cố này xảy ra trong thư viện NDK, công cụ trò chơi (Unity, Unreal) và phụ thuộc hệ thống. Native Crash KHÔNG bị chặn bởi Thread.setDefaultUncaughtExceptionHandler — nó tiêu diệt tiến trình ngay lập tức.

Để chẩn đoán sự cố gốc, sử dụng tệp minidump (Breakpad) hoặc tombstones Android. Firebase Crashlytics hỗ trợ thu thập sự cố gốc qua NDK SDK. Trên iOS, vấn đề tương tự được giải quyết bằng PLCrashReporter.

Công cụ báo cáo sự cố

Ba công cụ thống trị thị trường báo cáo sự cố di động. Mỗi công cụ cung cấp thu thập stack trace, tổng hợp theo phiên bản ứng dụng và thông báo về sự cố mới.

Firebase Crashlytics

Crashlytics là trình báo cáo sự cố phổ biến nhất cho ứng dụng di động, thuộc hệ sinh thái Firebase. Nó tự động thu thập stack trace, thông tin thiết bị, phiên bản HĐH và khóa tùy chỉnh của người dùng. Việc tích hợp mất 10 phút qua Firebase Console và Gradle Plugin. Crashlytics cũng hỗ trợ nhật ký thời gian thực (Logcat) và theo dõi người dùng.

kotlin
FirebaseCrashlytics.getInstance()
    .setCustomKey("current_screen", "ProfileFragment")

FirebaseCrashlytics.getInstance()
    .log("User tapped login button")

Sentry

Sentry là một giải pháp thay thế cho Crashlytics với hệ thống lọc linh hoạt hơn và hỗ trợ hơn 90 nền tảng. Không giống Firebase, Sentry cung cấp máy chủ tự lưu trữ (self-hosted) cho các công ty có yêu cầu nghiêm ngặt về dữ liệu. Sentry hỗ trợ theo dõi phân tán, breadcrumbs và tích hợp CI/CD.

Bugsnag và AppCenter

Bugsnag nổi bật với hỗ trợ cảnh báo dựa trên mức độ nghiêm trọng: phân loại sự cố thành critical, error và warning. AppCenter của Microsoft là công cụ miễn phí với chức năng cơ bản cho dự án nhỏ. Cả hai đều hỗ trợ Android, iOS, React Native và Flutter.

Cách phân tích sự cố

Phân tích sự cố là quá trình tái tạo bức tranh toàn cảnh về những gì đã xảy ra. Stack trace chỉ hiển thị điểm thất bại cuối cùng nhưng không cung cấp bối cảnh dẫn đến vấn đề. Một cách tiếp cận chuyên nghiệp bao gồm bốn giai đoạn.

Giai đoạn đầu tiên — đọc stack trace. Xác định lớp, phương thức và dòng mã nơi ngoại lệ xảy ra. Theo dõi chuỗi gọi từ khung trên xuống khung dưới: dòng cuối cùng trong stack là vị trí sập và các dòng trên là trình tự gọi. Giải mã (ánh xạ ProGuard/R8) là bắt buộc cho bản dựng sản xuất.

Giai đoạn thứ hai — bối cảnh thiết bị. Crashlytics hiển thị mẫu thiết bị, phiên bản HĐH, bộ nhớ khả dụng và phiên bản ứng dụng. Ví dụ, sự cố chỉ xảy ra trên Samsung Galaxy S10 với Android 11 cho thấy vấn đề với phiên bản One UI cụ thể, không phải lỗi mã chung.

Giai đoạn thứ ba — tái tạo trên thiết bị thử nghiệm. Nếu sự cố không tái tạo ổn định, hãy hỏi người dùng các bước chính xác hoặc sử dụng Remote Config để ghi nhật ký trước phần mã có vấn đề. Kiểm thử AB bản sửa lỗi trên một phần người dùng giúp xác nhận giải pháp.

Giai đoạn thứ tư — giám sát sau sửa lỗi. Sau khi phát hành bản sửa lỗi, giám sát tỷ lệ sập trong 3–5 ngày. Nếu sự cố biến mất hoàn toàn — bản sửa lỗi đã hiệu quả. Nếu tần suất giảm nhưng không về không — có kịch bản thứ hai cần phân tích riêng.

Thực hành ngăn chặn sự cố

Cách tiếp cận có hệ thống để ngăn chặn sự cố bao gồm công cụ phân tích tĩnh, kiểm thử trường hợp biên bắt buộc và xử lý lỗi đúng cách ở tất cả các cấp của ứng dụng.

Phân tích mã tĩnh

Detekt (Kotlin) và Lint (Android) tìm vấn đề tiềm ẩn tại thời điểm biên dịch: biến không sử dụng, NPE tiềm ẩn, sử dụng API sai. Đưa các công cụ này vào đường ống CI với ngưỡng lỗi. Ví dụ, Detekt với cấu hình 30+ cảnh báo hoặc bất kỳ lỗi chặn nào sẽ không cho phép xây dựng.

Kiểm thử đơn vị và kiểm thử UI

Bao phủ các kịch bản sử dụng chính bằng kiểm thử đơn vị là bảo vệ cơ bản chống lại sự cố hồi quy. Kiểm thử mô hình dữ liệu, ViewModel và lớp UseCase với các trường hợp biên: giá trị null, danh sách rỗng, JSON không hợp lệ. Kiểm thử UI qua Espresso hoặc Compose Test bao phủ các luồng quan trọng: xác thực, thanh toán, hướng dẫn.

Suy giảm mượt mà

Thiết kế ứng dụng sao cho lỗi trong một mô-đun không làm sập toàn bộ màn hình. Sử dụng khối catch ở cấp ViewModel với trạng thái dự phòng: hiển thị giữ chỗ thay vì danh sách, dữ liệu được lưu trong bộ nhớ đệm khi ngoại tuyến, hình ảnh dự phòng khi lỗi tải. Điều này biến sự cố tiềm ẩn thành kịch bản UX có kiểm soát.

Triển khai theo giai đoạn với giám sát

Triển khai theo giai đoạn là thực hành tiêu chuẩn trên Google Play và App Store: phiên bản mới được phát hành cho 5%, sau đó 20%, sau đó 100% người dùng với khoảng cách 1–3 ngày. Ở mỗi giai đoạn, tỷ lệ sập được giám sát: nếu tỷ lệ không sập giảm dưới 99,5%, việc triển khai tự động dừng lại. Firebase Remote Config cho phép tắt tính năng có vấn đề mà không cần phát hành phiên bản mới.

Kiểm soát phiên bản phụ thuộc

Renovate hoặc Dependabot trong CI tự động kiểm tra thư viện về các lỗ hổng đã biết và lỗi nghiêm trọng. Cập nhật một phụ thuộc duy nhất có thể loại bỏ toàn bộ một lớp sự cố. Tuy nhiên, hãy kiểm thử bản cập nhật trên môi trường staging trước khi triển khai vào sản xuất — phiên bản thư viện mới có thể chứa thay đổi không tương thích.

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

Có thể ngăn chặn 100% sự cố không?

Không. Một số sự cố do các yếu tố ngoài tầm kiểm soát của nhà phát triển: lỗi hệ thống, vấn đề phần cứng, không tương thích firmware. Mục tiêu là giảm tỷ lệ xuống 0,1% hoặc thấp hơn và giảm thiểu thời gian phản hồi cho các sự cố còn lại.

Trình báo cáo sự cố khác với phân tích như thế nào?

Trình báo cáo sự cố thu thập stack trace, trạng thái bộ nhớ và thông tin thiết bị tại thời điểm sập. Phân tích thu thập dữ liệu hành vi người dùng. Crashlytics kết hợp cả hai cách tiếp cận, cung cấp bối cảnh sự cố cùng với khóa tùy chỉnh của người dùng.

Tại sao stack trace bị làm rối?

ProGuard và R8 làm rối mã để bảo vệ tài sản trí tuệ. Để giải mã, hãy tải tệp ánh xạ lên Crashlytics khi phát hành. Nếu không có tệp ánh xạ, stack trace sẽ hiển thị a.a(), b.b() thay vì tên lớp và phương thức thực.

Làm thế nào trình báo cáo sự cố chặn ngoại lệ?

Thông qua Thread.setDefaultUncaughtExceptionHandler trên Android: thư viện đăng ký trình xử lý riêng của nó, nhận ngoại lệ không được xử lý trước, lưu dữ liệu và chỉ sau đó kết thúc tiến trình. Trên iOS, NSSetUncaughtExceptionHandler được sử dụng cho NSException và Mach exception handler cho tín hiệu.

Sự cố nghiêm trọng (fatal) và không nghiêm trọng (non-fatal) là gì?

Fatal — ứng dụng đã kết thúc. Non-fatal (ngoại lệ đã bắt) — nhà phát triển đã bắt ngoại lệ qua try-catch, nhưng nó có thể chỉ ra vấn đề tiềm ẩn. Crashlytics phân biệt các loại này và cho phép lọc non-fatal riêng biệt để tránh làm lộn xộn bảng điều khiển.

Tổng kết

  • Crash — kết thúc bất thường của ứng dụng do ngoại lệ không được xử lý hoặc tín hiệu nghiêm trọng
  • NullPointerException vẫn là loại sự cố phổ biến nhất trong ứng dụng di động
  • Firebase Crashlytics — công cụ tiêu chuẩn để thu thập và phân tích sự cố trong sản xuất
  • Phân tích sự cố bao gồm đọc stack trace, bối cảnh thiết bị và tái tạo trên môi trường thử nghiệm
  • Phân tích tĩnh (Detekt, Lint) ngăn chặn một số sự cố tại thời điểm biên dịch
  • Suy giảm mượt mà biến sự cố tiềm ẩn thành kịch bản có thể quản lý với dữ liệu dự phòng
  • Tệp ánh xạ bắt buộc để giải mã stack trace trong bản dựng sản xuấ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

Đọc thêm