ANR trong phát triển Android — nó là gì, nguyên nhân và cách khắc phục

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

ANR (Application Not Responding) là một thông báo hệ thống Android xuất hiện khi ứng dụng ngừng phản hồi đầu vào của người dùng trong hơn 5 giây. Theo Android Developers, nguyên nhân chính là các thao tác kéo dài trên luồng chính làm chặn xử lý chạm và hiển thị giao diện. Hiểu cơ chế ANR là cần thiết cho mọi nhà phát triển Android để tạo ra các ứng dụng phản hồi nhanh.

Những điểm chính

  • ANR — cảnh báo hệ thống Android khi ứng dụng bị đóng băng hơn 5 giây
  • Luồng chính (luồng UI) — nơi duy nhất mà việc chặn dẫn đến ANR
  • InputDispatcher — thành phần hệ thống phát hiện độ trễ đầu vào và kích hoạt ANR
  • traces.txt — tệp chính để chẩn đoán nguyên nhân đóng băng trên thiết bị
  • StrictMode — công cụ tích hợp Android để phát hiện các thao tác kéo dài trên luồng UI

ANR là gì

ANR (Application Not Responding) là một hộp thoại của hệ điều hành Android xuất hiện khi ứng dụng ngừng phản hồi đầu vào của người dùng. Hệ thống theo dõi thời gian xử lý sự kiện thông qua InputDispatcher: nếu một lần chạm hoặc nhấn nút không được xử lý trong vòng 5 giây, Android sẽ hiển thị hộp thoại đề nghị đóng hoặc chờ ứng dụng.

Cơ chế ANR bảo vệ trải nghiệm người dùng khỏi các ứng dụng bị đóng băng. Android không cho phép một ứng dụng chặn toàn bộ hệ thống — không giống như hệ điều hành máy tính, nền tảng di động buộc giới hạn thời gian xử lý sự kiện. BroadcastReceiver có giới hạn 10 giây và dịch vụ nền trước có 20 giây.

ANR KHÔNG phải là một ngoại lệ trong mã — đó là một cơ chế hệ thống ở cấp độ tiến trình Linux. Android gửi tín hiệu SIGQUIT đến tiến trình, sau đó hệ thống lưu ngăn xếp cuộc gọi của tất cả các luồng vào tệp traces.txt. Nhà phát triển nhận ANR không phải như một ngoại lệ catch, mà như một báo cáo sau khi ứng dụng khởi động lại. Trên Android 11+, API ApplicationExitInfo cho phép lấy lý do kết thúc tiến trình theo cách lập trình, bao gồm ANR — điều này đơn giản hóa việc thu thập thống kê mà không cần phân tích thủ công traces.txt.

Nguyên nhân chính của ANR

Năm loại thao tác liên tục dẫn đến ANR trong các ứng dụng Android. Mỗi loại chặn luồng chính, ngăn hệ thống xử lý các sự kiện đầu vào và vẽ lại màn hình.

Yêu cầu mạng trên luồng chính

Các yêu cầu HTTP đồng bộ được thực thi trên luồng UI là nguyên nhân phổ biến nhất của ANR ở các nhà phát triển mới bắt đầu. Ngay cả một yêu cầu nhanh đến máy chủ cũng có thể mất 1–3 giây và với kết nối kém — 30 giây hoặc hơn. Android rõ ràng cấm các thao tác mạng trên luồng chính từ API 11, ném ra NetworkOnMainThreadException.

Sử dụng Coroutines hoặc RxJava cho các cuộc gọi bất đồng bộ. Coroutine với bộ điều phối Dispatchers.IO thực thi yêu cầu trên luồng nền và chuyển kết quả đến luồng chính qua Dispatchers.Main. Điều này loại bỏ hoàn toàn việc chặn luồng UI bởi các thao tác mạng.

kotlin
fun fetchUserData() {
    CoroutineScope(Dispatchers.Main).launch {
        val result = withContext(Dispatchers.IO) {
            api.getUserData() // thao tác nền
        }
        updateUI(result) // kết quả trên luồng chính
    }
}

Tính toán chuyên sâu trên luồng UI

Xử lý mảng dữ liệu lớn, phân tích JSON hoặc XML, làm việc trực tiếp với bitmap trên luồng chính — nguyên nhân phổ biến thứ hai của ANR. Ngay cả 300 mili giây làm việc liên tục của luồng UI mà không quay lại vòng lặp sự kiện cũng gây ra độ trễ hiển thị đáng chú ý và ngưỡng 5 giây được ghi nhận là ANR.

WorkManager và các dịch vụ nền được thiết kế để chuyển các tính toán nặng ra khỏi luồng chính. Sử dụng AsyncTask (lỗi thời), ListenableFuture hoặc Kotlin Flow để truyền dữ liệu theo khối mà không chặn UI.

Khóa đồng bộ hóa và Deadlock

Deadlock xảy ra khi hai luồng giữ khóa và chờ đợi lẫn nhau. Nếu một trong các luồng là luồng chính, hệ thống ghi nhận ANR chính xác sau 5 giây. Thread.join(), CountDownLatch.await() và các khối synchronized được gọi từ luồng UI mang rủi ro chặn.

Tránh bất kỳ thao tác chặn nào trên luồng chính. Thay vì synchronized, hãy sử dụng ConcurrentHashMap; thay vì Thread.join() — coroutine với async/await. Quy tắc này áp dụng cho bất kỳ ngôn ngữ nào trên Android: Java, Kotlin hoặc C++ qua JNI.

BroadcastReceiver chạy lâu

BroadcastReceiver chạy trên luồng chính theo mặc định. Nếu onReceive() bận hơn 10 giây, Android hiển thị ANR. Tải dữ liệu từ cơ sở dữ liệu hoặc mạng bên trong onReceive là con đường chắc chắn dẫn đến đóng băng.

Sử dụng goAsync() bên trong BroadcastReceiver để chuyển sang luồng nền hoặc registerReceiver với getBackgroundBroadcastReceiver(). Điều này cho phép xử lý sự kiện mà không chặn UI.

ContentProvider và SQLite trên luồng chính

Các truy vấn nặng đến ContentProvider hoặc làm việc trực tiếp với SQLite trên luồng UI — một nguyên nhân ít rõ ràng nhưng phổ biến của ANR. Trong quá trình di chuyển cơ sở dữ liệu hoặc chèn hàng loạt hàng nghìn bản ghi, thời gian thực thi có thể vượt quá giới hạn 5 giây.

Di chuyển tất cả các thao tác cơ sở dữ liệu sang luồng nền bằng Room với các hàm suspend. Room tự động kiểm tra rằng truy vấn không được thực thi trên luồng chính và ném ra ngoại lệ nếu vi phạm.

Cách chẩn đoán ANR

Chẩn đoán ANR khác với gỡ lỗi các ngoại lệ thông thường — bạn không thể bắt ANR trong khối try-catch. Nguồn thông tin chính là tệp traces.txt, mà Android tạo tại thời điểm đóng băng.

traces.txt chứa ngăn xếp cuộc gọi của tất cả các luồng ứng dụng tại thời điểm ANR. Để đọc tệp từ thiết bị thực, hãy chạy lệnh adb bugreport, lệnh này thu thập báo cáo hệ thống đầy đủ bao gồm tất cả các ANR gần đây. Đối với trình giả lập, tệp có sẵn tại /data/anr/traces.txt. Ngăn xếp cuộc gọi cho thấy phương thức nào đang được thực thi trên luồng chính tại thời điểm chặn.

text
adb bugreport bugreport.zip
unzip -p bugreport.zip "*traces*" > traces.txt

Google Play Console cung cấp phần ANR & Crash với các báo cáo tổng hợp và tần suất lỗi. Đối với mỗi ANR, ngăn xếp cuộc gọi và thống kê thiết bị được hiển thị: mẫu máy, phiên bản Android, khu vực. Điều này cho phép xác định các ANR phụ thuộc vào thiết bị hoặc phiên bản hệ thống cụ thể.

Android Studio từ năm 2021 bao gồm ANR Watchdog trong trình phân tích. Nó tự động ghi lại các bản kết xuất luồng nếu luồng chính không phản hồi lâu hơn thời gian ngưỡng. Công cụ này hiển thị dòng thời gian sự kiện: thao tác nào đã được bắt đầu, phương thức nào đã được thực thi và sự chặn xảy ra ở giai đoạn nào.

Cách ngăn chặn ANR

Phòng ngừa ANR dựa trên một quy tắc cơ bản: luồng chính chỉ nên xử lý các sự kiện UI. Bất kỳ thao tác nào kéo dài hơn 16 mili giây (thời gian một khung hình) nên được thực thi trên luồng nền.

StrictMode — kiểm tra tự động

StrictMode là công cụ tích hợp Android để phát hiện các ANR tiềm ẩn trong quá trình phát triển. Kích hoạt nó trong Application.onCreate() với các cờ cho thao tác đĩa và mạng. Khi vi phạm, StrictMode ném ra ngoại lệ hoặc ghi vào logcat.

kotlin
if (BuildConfig.DEBUG) {
    StrictMode.ThreadPolicy.Builder()
        .detectDiskReads()
        .detectDiskWrites()
        .detectNetwork()
        .penaltyLog()
        .build()
        .let { StrictMode.setThreadPolicy(it) }
}

Các mẫu bất đồng bộ: Coroutines và RxJava

Kotlin Coroutines — cách tiêu chuẩn để làm việc bất đồng bộ trong các ứng dụng Android hiện đại. Cách tiếp cận chính: các thao tác I/O chạy trên Dispatchers.IO, kết quả được chuyển đến Dispatchers.Main để cập nhật UI. Đối với các kịch bản giống Flow, hãy sử dụng Dispatchers.Default cho các tác vụ chuyên sâu CPU.

RxJava vẫn phổ biến trong các dự án cũ. subscribeOn(Schedulers.io()) và observeOn(AndroidSchedulers.mainThread()) — bộ tối thiểu để ngăn chặn ANR. Quy tắc chính cũng giống vậy: không Observable hay Flowable nào nên phát dữ liệu từ luồng chính.

Giám sát trong sản xuất

Firebase Crashlytics từ phiên bản SDK 18.4.0 hỗ trợ giám sát ANR sẵn có. Đối với Android 11+, Crashlytics sử dụng API hệ thống ApplicationExitInfo, cung cấp lý do kết thúc chính xác: ANR, Crash hoặc bị hệ thống tiêu diệt. Kích hoạt các khóa tùy chỉnh với tham số màn hình và trạng thái để phân tích ngữ cảnh.

Công cụ phát hiện ANR

Năm công cụ bao phủ tất cả các giai đoạn làm việc với ANR: từ gỡ lỗi trên máy trạm đến giám sát trong sản xuất. Mỗi công cụ giải quyết nhiệm vụ riêng và cung cấp dữ liệu cho các kịch bản khác nhau.

Công cụMục đíchĐịnh dạng dữ liệu
StrictModePhát hiện trong phát triểnLogcat / Exception
ANR Watchdog (Android Studio)Theo dõi thời gian thựcThread dump + timeline
Google Play ConsoleThống kê tổng hợpANR rate + stack traces
Firebase CrashlyticsGiám sát sản xuấtApplicationExitInfo
adb bugreportBáo cáo hệ thống đầy đủtraces.txt + logcat + dmesg

Mỗi công cụ có lĩnh vực riêng: StrictMode phát hiện các vi phạm rõ ràng ở giai đoạn đầu, Crashlytics hiển thị tần suất ANR thực tế giữa người dùng và adb bugreport cung cấp hình ảnh đầy đủ nhất cho các trường hợp phức tạp. Kết hợp chúng để có phạm vi bao phủ đầy đủ.

Firebase Performance Monitoring

Firebase Performance theo dõi thời gian phản hồi của luồng UI và tự động tạo dấu vết cho các thao tác nghi ngờ kéo dài. Nếu luồng chính bị chặn hơn 500 ms, Performance ghi lại một dấu vết tùy chỉnh với tên của phương thức gây ra. Điều này cho phép phát hiện các kịch bản ANR mà không cần sự tham gia của người dùng và trước khi chúng trở nên nghiêm trọng.

Tích hợp với Firebase Crashlytics mang lại bức tranh đầy đủ: Performance hiển thị sự chậm lại trước ANR và Crashlytics hiển thị sự đóng băng. Thiết lập cảnh báo trong Firebase Console cho tỷ lệ ANR trên 0.1% và bạn sẽ nhận được thông báo về các vấn đề mới trước khi có khiếu nại hàng loạt từ người dùng.

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

ANR khác với Crash như thế nào?

ANR là tình trạng đóng băng khi ứng dụng không phản hồi nhưng vẫn ở trong bộ nhớ. Crash là kết thúc bất thường hoàn toàn với việc thoát tiến trình. ANR có thể “sống sót” nếu hệ thống hoặc người dùng chờ phản hồi, trong khi Crash luôn kết thúc ứng dụng.

Có thể bắt ANR qua try-catch không?

Không. ANR không phải là ngoại lệ Java/Kotlin, mà là tín hiệu hệ thống ở cấp tiến trình (SIGQUIT). Nhà phát triển không thể xử lý nó trong mã ứng dụng. Cách duy nhất để phản ứng với ANR là phân tích báo cáo sau khi khởi động lại.

Tại sao ANR xuất hiện trên một số thiết bị nhưng không xuất hiện trên các thiết bị khác?

Hiệu suất thiết bị, phiên bản Android, tải CPU và số lượng tiến trình nền ảnh hưởng đến khả năng xảy ra ANR. Trên các thiết bị yếu, cùng một thao tác có thể mất gấp 2–3 lần thời gian, vượt quá giới hạn 5 giây.

Giới hạn thời gian của BroadcastReceiver trước ANR là bao nhiêu?

10 giây cho BroadcastReceiver thông thường trong onReceive(). Đối với dịch vụ nền trước, giới hạn là 20 giây và đối với ContentProvider — không có giới hạn rõ ràng, nhưng chặn luồng chính hơn 5 giây vẫn gây ra ANR.

Phải làm gì nếu ANR hiếm khi xảy ra và không thể tái tạo?

Kích hoạt StrictMode trong tất cả các bản dựng gỡ lỗi, thêm giám sát qua Firebase Crashlytics và sử dụng adb bugreport khi ANR xảy ra. ANR không thường xuyên thường liên quan đến điều kiện cạnh tranh hoặc trạng thái mạng cụ thể.

Tổng kết

  • ANR — cơ chế hệ thống Android được kích hoạt khi luồng chính bị chặn hơn 5 giây
  • Luồng chính chỉ nên xử lý UI — tất cả các thao tác khác được chuyển sang luồng nền
  • Chẩn đoán ANR được thực hiện qua traces.txt, Google Play Console và Firebase Crashlytics
  • StrictMode phát hiện các ANR tiềm ẩn trong quá trình phát triển mà không cần chạy trên thiết bị thực
  • Coroutines với Dispatchers.IO — cách tiêu chuẩn để làm việc bất đồng bộ trong các dự án Android hiện đại
  • BroadcastReceiver yêu cầu goAsync() hoặc trình đăng ký nền để hoạt động hơn 10 giây
  • ANR trong sản xuất được giám sát qua Crashlytics và API ApplicationExitInfo tích hợp trên Android 11 trở lên

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