Khi luồng giao diện người dùng của một ứng dụng Android bị chặn quá lâu, lỗi "Ứng dụng không phản hồi" (ANR) sẽ được kích hoạt. Nếu ứng dụng chạy ở nền trước, hệ thống sẽ hiển thị hộp thoại cho người dùng như minh hoạ trong hình 1. Hộp thoại ANR cho phép người dùng buộc thoát khỏi ứng dụng.
ANR là một sự cố của luồng chính trong ứng dụng, vì luồng này chịu trách nhiệm cập nhật giao diện người dùng nhưng không thể xử lý các sự kiện đầu vào của người dùng hoặc vẽ khiến người dùng thất vọng. Để biết thêm thông tin về luồng chính của ứng dụng, hãy xem bài viết Tổng quan về quy trình và luồng.
ANR được kích hoạt cho ứng dụng của bạn khi một trong các điều kiện sau xảy ra:
- Hết thời gian chờ điều phối đầu vào: Nếu ứng dụng của bạn chưa phản hồi một sự kiện đầu vào (chẳng hạn như thao tác nhấn phím hoặc chạm vào màn hình) trong vòng 5 giây.
- Thực thi dịch vụ: Nếu một dịch vụ do ứng dụng của bạn khai báo không thể hoàn tất quá trình thực thi
Service.onCreatevàService.onStartCommand/Service.onBindtrong vòng vài giây. Service.startForegroundkhông được gọi: Nếu ứng dụng của bạn sử dụngContext.startForegroundServiceđể bắt đầu một dịch vụ mới trên nền trước nhưng dịch vụ đó không gọistartForegroundtrong vòng 5 giây.- Truyền đi ý định: Nếu
BroadcastReceiverchưa hoàn tất quá trình thực thi trong một khoảng thời gian nhất định. Nếu ứng dụng có bất kỳ hoạt động nào ở nền trước, thời gian chờ này là 5 giây. - Tương tác
JobScheduler: NếuJobServicekhông trả về từJobService.onStartJobhoặcJobService.onStopJobtrong vòng vài giây, hoặc nếu lệnh do người dùng yêu cầu bắt đầu và ứng dụng của bạn không gọiJobService.setNotificationtrong vòng vài giây sau khiJobService.onStartJobđược gọi. Đối với các ứng dụng nhắm đến Android 13 trở xuống, lỗi ANR không hoạt động và không được báo cáo cho ứng dụng. Đối với các ứng dụng nhắm đến Android 14 trở lên, lỗi ANR phải rõ ràng và được báo cáo cho ứng dụng.
Nếu ứng dụng của bạn gặp các lỗi ANR, bạn có thể sử dụng hướng dẫn trong tài liệu này để chẩn đoán và khắc phục vấn đề.
Phát hiện vấn đề
Nếu đã phát hành ứng dụng, bạn có thể sử dụng Android vitals để xem thông tin về lỗi ANR cho ứng dụng của mình. Bạn có thể dùng các công cụ khác để phát hiện lỗi ANR trong trường này, nhưng hãy lưu ý rằng, khác với Android vitals, công cụ bên thứ ba không thể báo cáo lỗi ANR trên Android 10 trở xuống.
Android vitals
Android vitals có thể giúp bạn theo dõi và cải thiện tỷ lệ ANR của ứng dụng. Android vitals đo lường một số tỷ lệ ANR:
- Tỷ lệ ANR: Tỷ lệ phần trăm số người dùng hoạt động hằng ngày gặp phải bất kỳ loại lỗi ANR nào.
- Tỷ lệ lỗi ANR mà người dùng nhận thấy: Là tỷ lệ phần trăm số người dùng hoạt động hằng ngày gặp phải ít nhất 1 lỗi ANR mà người dùng nhận thấy. Hiện tại, chỉ những lỗi ANR thuộc loại
Input dispatching timed outđược coi là do người dùng nhận thấy. - Tỷ lệ ANR nhiều lần: Là tỷ lệ phần trăm người dùng hoạt động hằng ngày gặp phải ít nhất 2 lỗi ANR.
Người dùng hoạt động hằng ngày là người dùng riêng biệt dùng ứng dụng của bạn trong một ngày trên một thiết bị, có thể có nhiều phiên hoạt động. Nếu một người dùng sử dụng ứng dụng của bạn trên nhiều thiết bị trong một ngày, thì từng thiết bị đó cũng sẽ đóng góp vào số lượng người dùng đang hoạt động trong ngày đó.
Tỷ lệ lỗi ANR mà người dùng nhận thấy là một chỉ số quan trọng chính, có nghĩa là tỷ lệ này ảnh hưởng đến khả năng người dùng phát hiện được ứng dụng của bạn trên Google Play. Đây là một chỉ số quan trọng vì các lỗi ANR mà chỉ số này tính đến luôn xảy ra khi người dùng tương tác với ứng dụng, do đó gây gián đoạn nhiều nhất.
Play đã xác định 2 ngưỡng hành vi xấu cho chỉ số này:
- Ngưỡng hành vi xấu chung: Ít nhất 0,47% số người dùng hoạt động hằng ngày gặp phải lỗi ANR mà người dùng nhận thấy trên tất cả các kiểu máy.
- Ngưỡng hành vi xấu trên mỗi thiết bị: Ít nhất 8% số người dùng hoạt động hằng ngày gặp phải lỗi ANR mà người dùng nhận thấy, đối với một kiểu máy duy nhất.
Nếu ứng dụng của bạn vượt quá ngưỡng hành vi xấu chung, thì ứng dụng đó có thể khó được phát hiện hơn trên tất cả các thiết bị. Nếu ứng dụng của bạn vượt quá ngưỡng hành vi xấu trên mỗi thiết bị ở một số thiết bị, thì ứng dụng đó có thể khó được phát hiện hơn trên các thiết bị đó và cảnh báo có thể sẽ xuất hiện trên trang thông tin của bạn trên Cửa hàng Play.
Android vitals có thể cảnh báo cho bạn thông qua Play Console khi ứng dụng của bạn gặp quá nhiều lỗi ANR.
Để biết thông tin về cách Google Play thu thập dữ liệu Android vitals, vui lòng xem tài liệu về Play Console.
Chẩn đoán lỗi ANR (Ứng dụng không phản hồi)
Có một vài mẫu phổ biến cần xem xét khi chẩn đoán lỗi ANR:
- Ứng dụng đang thực hiện các thao tác chậm liên quan đến I/O trên luồng chính.
- Ứng dụng đang thực hiện một phép tính mất nhiều thời gian trên luồng chính.
- Luồng chính đang thực hiện một lệnh gọi liên kết đồng bộ tới một quy trình khác, và quy trình này sẽ mất nhiều thời gian để trả lệnh về lại.
- Luồng chính bị chặn đang chờ một khối đồng bộ hoá cho hoạt động mất nhiều thời gian đang diễn ra trên luồng khác.
- Luồng chính đang bị tắc nghẽn với một luồng khác, trong quá trình hoặc thông qua một lệnh gọi liên kết. Luồng chính không những đang đợi một hoạt động mất nhiều thời gian kết thúc mà còn đang gặp tình huống tắc nghẽn.
Các kỹ thuật sau có thể giúp bạn xác định nguyên nhân dẫn đến lỗi ANR.
HealthStats
HealthStats cung cấp các chỉ số về trạng thái của ứng dụng bằng cách ghi lại tổng thời gian người dùng và hệ thống, thời gian của CPU, số liệu thống kê về đài, mạng, thời gian bật/tắt màn hình và chuông báo thức. Thông tin này có thể giúp bạn đo lường mức sử dụng CPU tổng thể và mức tiêu hao pin.
Gỡ lỗi
Debug giúp bạn kiểm tra các ứng dụng Android trong quá trình phát triển, bao gồm cả việc theo dõi và phân bổ số lượng để xác định hiện tượng giật và độ trễ trong ứng dụng.
Bạn cũng có thể sử dụng Debug để xem bộ đếm thời gian chạy và bộ nhớ gốc, cũng như các chỉ số về bộ nhớ có thể giúp bạn xác định mức sử dụng bộ nhớ của một quy trình cụ thể.
ApplicationExitInfo
ApplicationExitInfo có trên Android 11 (API cấp 30) trở lên và cung cấp thông tin về lý do thoát khỏi ứng dụng. Lý do bao gồm lỗi ANR, sắp hết bộ nhớ, ứng dụng gặp sự cố, sử dụng CPU quá mức, tình trạng gián đoạn do người dùng, gián đoạn do hệ thống và các thay đổi quyền khi bắt đầu chạy.
Chế độ nghiêm ngặt
Việc sử dụng StrictMode sẽ giúp bạn tìm thấy các hoạt động I/O ngẫu nhiên trên luồng chính khi đang phát triển ứng dụng của mình. Bạn có thể dùng StrictMode ở cấp ứng dụng hoặc hoạt động.
Bật hộp thoại lỗi ANR trong nền
Android sẽ hiển thị hộp thoại lỗi ANR cho các ứng dụng mất quá nhiều thời gian để xử lý thông báo phát đi với điều kiện mục Hiển thị tất cả các lỗi ANR được bật trong phần Tuỳ chọn cho nhà phát triển trên thiết bị. Vì lý do này, hộp thoại lỗi ANR trong nền không phải lúc nào cũng hiển thị cho người dùng, ngay cả khi ứng dụng gặp vấn đề về hiệu suất.
Điểm tắc nghẽn trong quá trình kết hợp lại
Sử dụng Trình phân tích tài nguyên Android Studio và Layout Inspector để theo dõi các nút thắt cổ chai của quá trình kết hợp lại. Để biết thêm thông tin, hãy xem bài viết Hiệu suất của Jetpack Compose.
Kéo tệp theo dõi
Android lưu trữ thông tin theo dõi khi gặp lỗi ANR. Trên các bản phát hành hệ điều hành cũ, chỉ có một tệp /data/anr/traces.txt trên thiết bị. Trên các bản phát hành hệ điều hành mới hơn, có nhiều tệp /data/anr/anr_*. Bạn có thể truy cập các dấu vết của lỗi ANR từ một thiết bị hoặc trình mô phỏng bằng cách sử dụng Cầu gỡ lỗi Android (adb) làm thư mục gốc:
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
Bạn có thể ghi lại báo cáo lỗi từ một thiết bị thực bằng cách sử dụng tuỳ chọn Tạo báo cáo lỗi cho nhà phát triển trên thiết bị hoặc lệnh adb bugreport trên máy phát triển của mình. Để biết thêm thông tin, hãy xem bài viết Ghi lại và đọc báo cáo lỗi.
Khắc phục sự cố
Sau khi đã xác định sự cố, bạn có thể sử dụng các mẹo trong phần này để khắc phục các sự cố thường gặp.
Mã chậm trên luồng chính
Xác định các vị trí trong mã của bạn nơi luồng chính của ứng dụng bận trong hơn 5 giây. Tìm các trường hợp sử dụng đáng ngờ trong ứng dụng của bạn và cố gắng mô phỏng lỗi ANR.
Một vấn đề thường gặp là tác vụ chạy trong thời gian dài ngay trong một thành phần kết hợp:
@Composable
fun BadList(rawStrings: List<String>) {
// Math or sorting inside the composable runs on EVERY recomposition pass!
val heavilyProcessedList = rawStrings
.filter { it.isNotBlank() }
.map { it.uppercase().reversed() }
.map { it.computationallyHeavyFunction() }
.sortedBy { it.length }
LazyColumn { items(sortedList) { Text(it) } }
}
// Modern Compose-first fix
@Composable
fun GoodList(viewModel: MyViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// UI simply renders state; no heavy processing allowed here
LazyColumn { items(uiState.sortedData) { Text(it) } }
}
I/O trên luồng chính
Việc thực thi các thao tác I/O trên luồng chính là nguyên nhân phổ biến gây ra các thao tác chậm trên luồng chính, điều này dẫn đến các lỗi ANR. Trong Compose, các nhà phát triển thường vô tình kích hoạt các hoạt động đọc dữ liệu trên ổ đĩa (chẳng hạn như SharedPreferences hoặc lệnh gọi cơ sở dữ liệu) trong khi cố gắng lấy trạng thái ban đầu.
Thực thi các thao tác I/O chạy trong thời gian dài bên ngoài lớp giao diện người dùng. Sử dụng withContext(Dispatchers.IO) trong ViewModel hoặc tốt hơn là sử dụng Repository trên lớp dữ liệu.
Tình trạng tắc nghẽn
Tình trạng tắc nghẽn sẽ xảy ra khi một luồng chuyển sang trạng thái chờ vì một tài nguyên cần thiết được giữ lại trong một luồng khác, đồng thời đang chờ tài nguyên do luồng đầu tiên lưu giữ. Nếu luồng chính của ứng dụng rơi vào trường hợp này, thì nhiều khả năng lỗi ANR sẽ xảy ra.
Tắc nghẽn là một hiện tượng được nghiên cứu kỹ lưỡng trong khoa học máy tính và có các thuật toán ngăn chặn cụ thể mà bạn có thể sử dụng để tránh tình trạng này.
Để biết thêm thông tin, hãy xem bài viết về Tắc nghẽn và Thuật toán ngăn ngừa tắc nghẽn trên Wikipedia.
Khi sử dụng Kotlin và Compose, bạn có thể thay thế các khoá nguyên thuỷ bằng Mutexes coroutine không chặn (Mutex.withLock) để ngăn chặn việc chặn luồng bằng cách tạm ngưng ngữ cảnh thực thi thay vì đóng băng luồng giao diện người dùng. Ví dụ:
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
// Modern non-blocking concurrency state architecture
class SecureDataRepository {
private val mutex = Mutex()
suspend fun safeUIAccess() {
// If locked, the main thread suspends seamlessly, preventing an ANR
mutex.withLock {
performSafeOperation()
}
}
}
Broadcast receiver (bộ thu phát sóng) chậm
Các ứng dụng có thể phản hồi thông báo phát đi, chẳng hạn như bật hoặc tắt chế độ trên máy bay hoặc sự thay đổi trạng thái kết nối bằng broadcast receiver. ANR xảy ra khi ứng dụng mất quá nhiều thời gian để xử lý thông báo phát đi.
Lỗi ANR xảy ra trong các trường hợp sau:
- Một bộ nhận tín hiệu truyền tin vẫn chưa hoàn tất việc thực thi phương thức
onReceivetrong khoảng thời gian đáng kể. - Bộ nhận tín hiệu truyền tin gọi
goAsyncnhưng không gọi đượcfinishtrên đối tượngPendingResult.
Ứng dụng của bạn chỉ được thực hiện các thao tác ngắn trong phương thức onReceive của BroadcastReceiver. Tuy nhiên, nếu ứng dụng của bạn yêu cầu xử lý phức tạp hơn do có thông báo phát đi, bạn nên chuyển nhiệm vụ đó cho ViewModel (tận dụng sức mạnh của các coroutine, phạm vi và trình điều phối Kotlin) nếu nhiệm vụ dự kiến mất tối đa vài giây, bất kỳ loại phần tử giữ trạng thái nào hoặc cho WorkManager đối với những nhiệm vụ dự kiến mất nhiều hơn vài giây.
GameActivity
Thư viện GameActivity đã giảm lỗi ANR trong các nghiên cứu điển hình về trò chơi và ứng dụng được viết bằng C hoặc C++. Nếu thay thế hoạt động gốc hiện có bằng GameActivity, bạn có thể giảm tình trạng chặn luồng giao diện người dùng và ngăn chặn một số lỗi ANR xảy ra.
Để biết thêm thông tin về lỗi ANR, hãy xem bài viết Duy trì khả năng thích ứng của ứng dụng. Để biết thêm thông tin về luồng, hãy xem phần Hiệu suất tốt hơn thông qua luồng.
Tài nguyên khác
Xem nội dung
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Đánh thức quá nhiều lần