ANR (Application Not Responding) là một thông báo hệ thống trong Android xuất hiện khi ứng dụng không phản hồi đầu vào trong vòng 5 giây. Khác với lỗi vặt (lỗi logic không chặn UI) và lag (chậm mà không dừng hoàn toàn), ANR là một lỗi nghiêm trọng được hệ điều hành ghi lại: Android hiển thị hộp thoại “Ứng dụng không phản hồi” với tùy chọn đóng hoặc chờ. Theo Android Vitals Documentation, các ứng dụng có tỷ lệ ANR trên 0,5% sẽ bị xếp hạng thấp hơn trên Google Play và có thể bị ẩn khỏi các đề xuất. Chẩn đoán bao gồm phân tích /data/anr/traces.txt, sử dụng StrictMode và cấu hình hồ sơ luồng chính.
Những điểm chính
ANR (Application Not Responding) là một cơ chế bảo vệ người dùng trong Android được kích hoạt khi ứng dụng ngừng phản hồi đầu vào. Hệ thống theo dõi thời gian xử lý sự kiện: nếu BroadcastReceiver không hoàn thành onReceive trong 10 giây, Service không trở về từ onCreate trong 20 giây, hoặc ContentProvider không phản hồi trong 15 giây — Android tạo ra ANR.
Khi ANR xảy ra, Android hiển thị hộp thoại hệ thống trên tất cả cửa sổ: “Ứng dụng không phản hồi. Bạn muốn đóng hay chờ?” Người dùng có thể đóng ứng dụng hoặc chờ nó khôi phục. Nếu ANR xảy ra thường xuyên, người dùng sẽ gỡ cài đặt ứng dụng. Google Play xem xét tỷ lệ ANR — phần trăm phiên có ANR — trong các thuật toán xếp hạng của mình.
iOS không có tương đương ANR với hộp thoại hệ thống. Thay vào đó, Apple sử dụng Watchdog, kết thúc tiến trình với mã thoát 0x8badf00d. Người dùng không thấy hộp thoại — ứng dụng chỉ đơn giản đóng lại về màn hình chính. Điều này làm cho ANR trên Android dễ thấy hơn đối với người dùng nhưng cung cấp cho hệ thống nhiều thông tin chẩn đoán hơn.
ANR xảy ra khi hệ thống theo dõi thời gian chờ cho một trong bốn loại thành phần. Mỗi thành phần có giới hạn thời gian riêng.
BroadcastReceiver được thực thi trong luồng chính. Nếu onReceive bắt đầu một yêu cầu mạng đồng bộ, ghi dài vào cơ sở dữ liệu hoặc chờ khóa — ANR sẽ xảy ra trong vòng 10 giây. Giải pháp: sử dụng goAsync() và WorkManager để xử lý nền. Kịch bản điển hình là nhận thông báo Push từ FCM và lưu đồng bộ vào Room.
Service.onCreate và Service.onStartCommand có giới hạn 20 giây. Nếu dịch vụ bắt đầu khởi tạo nặng (tải thư viện, đọc cấu hình từ mạng) trong luồng chính — ANR là không thể tránh khỏi. Sử dụng IntentService (lỗi thời) hoặc WorkManager để thực thi đảm bảo trong nền.
ContentProvider.onCreate được thực thi trước Application.onCreate và có giới hạn 15 giây. Nếu nhà cung cấp thực hiện di chuyển cơ sở dữ liệu, tải từ điển hoặc khởi tạo SDK từ mạng — điều này gây ra ANR khi khởi động ứng dụng. Giải pháp: khởi tạo trễ, chuyển các thao tác nặng sang WorkManager.
Android cung cấp một số công cụ để phân tích ANR: từ nhật ký hệ thống đến các thư viện chuyên dụng.
Ở mỗi ANR, Android lưu tệp /data/anr/traces.txt với kết xuất ngăn xếp của tất cả các luồng ứng dụng. Tìm luồng “main” — phương thức cuối cùng trong ngăn xếp chỉ ra nguyên nhân. Các mẫu điển hình: Thread.sleep(), InputStream.read(), BinderProxy.transact(). Để trích xuất tệp từ thiết bị, sử dụng adb với quyền siêu người dùng.
Firebase Crashlytics tự động thu thập ANR và hiển thị chúng trong bảng điều khiển với dấu vết. Đối với Android 11+, báo cáo ANR đi kèm với ngăn xếp đầy đủ của luồng chính. Tích hợp yêu cầu thêm phụ thuộc và khởi tạo FirebaseApp trong Application.onCreate.
CPU Profiler trong Android Studio cho phép bạn ghi lại dấu vết ứng dụng và xem phương thức nào tiêu tốn thời gian CPU. Bật “Record with method traces” và tái tạo kịch bản gây ra ANR. Dòng thời gian sẽ hiển thị phương thức nào đang chạy trong luồng chính tại thời điểm treo.
Ví dụ tích hợp Firebase Crashlytics để thu thập ANR trên Android:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(true)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
}
Khắc phục ANR chủ yếu có nghĩa là di chuyển tất cả các thao tác dài từ luồng chính sang các luồng nền. Hãy xem các kỹ thuật cụ thể cho từng loại thành phần.
WorkManager là giải pháp được Google khuyên dùng cho công việc nền. Nó đảm bảo thực thi tác vụ trong luồng nền có tính đến trạng thái thiết bị. Khác với Service, WorkManager không chặn luồng chính và có khả năng chống chịu với việc khởi động lại ứng dụng. Đối với BroadcastReceiver, sử dụng goAsync() và chuyển PendingResult cho WorkManager.
Chạy tất cả các yêu cầu mạng, thao tác cơ sở dữ liệu và I/O tệp với Dispatchers.IO. Luồng chính chỉ nên cập nhật UI. Sử dụng viewModelScope để tự động hủy coroutine khi Activity bị phá hủy. Tránh runBlocking() trong bất kỳ ngữ cảnh nào — nó chặn luồng hiện tại một cách đồng bộ.
Nếu ContentProvider thực hiện khởi tạo chậm, hãy sử dụng cơ chế tải trễ: tạo một nhà cung cấp trả về dữ liệu ngay lập tức và bắt đầu khởi tạo nặng thông qua WorkManager với độ trễ. Điều này ngăn chặn ANR khi khởi động ứng dụng, khi hệ thống nhạy cảm nhất với độ trễ.
Ví dụ sử dụng đúng BroadcastReceiver với goAsync trong Android:
class FcmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val pendingResult = goAsync()
WorkManager.getInstance(context!!)
.enqueue(OneTimeWorkRequest.from(NotificationWorker::class.java))
pendingResult.finish()
}
}
Cách tốt nhất để chống lại ANR là ngăn chặn chúng trong quá trình phát triển thông qua các công cụ và quyết định kiến trúc.
StrictMode với các chính sách detectNetwork() và detectDiskReads()/detectDiskWrites() được bật sẽ xác định các ANR tiềm ẩn trong quá trình phát triển. Trong bản dựng Debug, đặt penaltyDeath — bất kỳ vi phạm nào sẽ gây ra sự cố ngay lập tức và nhà phát triển sẽ thấy vấn đề trước khi commit.
Firebase Performance theo dõi thời gian thực thi của các thao tác chính và hiển thị kịch bản nào vượt quá ngưỡng ANR. Thiết lập dấu vết tùy chỉnh cho mỗi màn hình và yêu cầu mạng. Nếu thời gian thực thi vượt quá 3 giây — đó là ANR tiềm ẩn cần tối ưu hóa.
Mô phỏng các điều kiện chậm: giới hạn tốc độ mạng bằng Network Link Conditioner trên iOS hoặc Android Emulator. Làm chậm đọc đĩa thông qua mô phỏng bộ nhớ chậm. ANR thường xuất hiện chính xác trong những điều kiện này; trên các thiết bị phát triển nhanh, chúng không nhìn thấy.
Câu hỏi thường gặp
Android theo dõi thời gian xử lý sự kiện trên luồng chính một cách rõ ràng và hiển thị hộp thoại ANR. iOS sử dụng Watchdog, đóng cưỡng bức ứng dụng khi nó treo hơn 10–20 giây. ANR là một đặc điểm của kiến trúc Android nơi nhiều thành phần (BroadcastReceiver, Service) có thời gian chờ nghiêm ngặt.
Trên Android 11+, bạn có thể lấy kết xuất ANR qua adb shell dumpsys dropbox --print data_app_anr. Trên Android 10 trở xuống, không có quyền root thì không thể truy cập /data/anr/traces.txt. Sử dụng Firebase Crashlytics — nó tự động thu thập báo cáo ANR cho Android 11+.
Google Play khuyên nghị tỷ lệ ANR dưới 0,5% — không quá 5 ANR trên 1000 phiên. Ứng dụng có tỷ lệ trên 1% sẽ nhận được cảnh báo trong Google Play Console và có thể bị ẩn khỏi các đề xuất. Lý tưởng nhất, tỷ lệ ANR nên dưới 0,1%.
Bản thân coroutine không chặn luồng. Nhưng nếu bên trong coroutine bạn thực thi runBlocking trên luồng chính hoặc coroutine được khởi chạy với Dispatchers.Main và thực hiện thao tác CPU dài — điều này sẽ gây ra ANR. Sử dụng Dispatchers.IO cho I/O và Dispatchers.Default cho tính toán.
Sử dụng Android Emulator với hồ sơ “Slow Network” hoặc viết một bài kiểm thử gọi Thread.sleep(6000) trên luồng chính. Khởi chạy ứng dụng qua Debug và sau 5 giây bạn sẽ thấy hộp thoại ANR. Kiểm tra rằng logcat hiển thị bản ghi ANR với dấu vết.
Tổng kế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.
Đọc thêm