Khi luồng giao diện người dùng của một ứng dụng Android bị chặn quá lâu, hệ thống sẽ gửi lỗi "Ứng dụng không phản hồi" (ANR). Trang này mô tả nhiều loại lỗi ANR, cách chẩn đoán các lỗi này và những đề xuất cách khắc phục lỗi. Tất cả các khoảng thời gian của thời gian chờ mặc định được liệt kê đều dành cho Dự án nguồn mở Android (AOSP) và các thiết bị Pixel; những khoảng thời gian này có thể khác nhau tuỳ theo Nhà sản xuất thiết bị gốc (OEM).
Xin lưu ý rằng khi xác định nguyên nhân gây ra lỗi ANR, bạn cần phân biệt giữa các vấn đề xảy ra với hệ thống và vấn đề xảy ra với ứng dụng.
Khi hệ thống ở trạng thái không tốt, những vấn đề sau có thể gây ra lỗi ANR:
- Các vấn đề tạm thời xảy ra ở máy chủ hệ thống khiến các lệnh gọi liên kết vốn thường nhanh sẽ bị chậm.
- Các vấn đề xảy ra với máy chủ hệ thống và mức tải cao trên thiết bị khiến các luồng ứng dụng không được lên lịch.
Nếu có thể, bạn nên sử dụng dấu vết Perfetto để phân biệt giữa các vấn đề xảy ra với hệ thống và vấn đề xảy ra với ứng dụng:
- Xem liệu luồng chính của ứng dụng có được lên lịch hay không bằng cách xem theo dõi trạng thái luồng trong Perfetto để biết luồng đó đang chạy hay có khả năng sẽ chạy.
- Xem các luồng
system_server
để phát hiện ra các vấn đề, chẳng hạn như tình trạng tranh chấp khoá. - Đối với lệnh gọi liên kết bị chậm, hãy xem luồng trả lời (nếu có) để biết nguyên nhân khiến lệnh gọi này bị chậm.
Hết thời gian chờ truyền dữ liệu đầu vào
Lỗi ANR khi truyền dữ liệu đầu vào xảy ra khi luồng chính của ứng dụng không phản hồi kịp thời một sự kiện đầu vào, chẳng hạn như thao tác vuốt hoặc nhấn phím. Vì ứng dụng chạy ở nền trước khi hết thời gian chờ truyền dữ liệu đầu vào, nên người dùng hầu như luôn thấy được lỗi này và do đó, việc giảm thiểu lỗi là điều rất quan trọng.
Khoảng thời gian chờ mặc định: 5 giây.
Lỗi ANR khi truyền dữ liệu đầu vào thường do các vấn đề trên luồng chính gây ra. Nếu luồng chính bị chặn khi đang chờ lấy một khoá, thì luồng phần tử giữ cũng có thể có liên quan.
Để tránh các lỗi ANR khi truyền dữ liệu đầu vào, hãy làm theo các phương pháp hay nhất sau đây:
- Không thực hiện các thao tác chặn hoặc các hoạt động diễn ra trong thời gian dài trên luồng chính. Hãy cân nhắc dùng
StrictMode
để phát hiện hoạt động do nhầm lẫn trên luồng chính. - Giảm thiểu tranh chấp khoá giữa luồng chính và các luồng khác.
- Giảm thiểu thao tác không liên quan đến giao diện người dùng trên luồng chính, chẳng hạn như khi xử lý thông báo truyền tin hoặc chạy các dịch vụ.
Nguyên nhân thường gặp
Dưới đây là một số nguyên nhân thường gặp và cách khắc phục được đề xuất cho lỗi ANR khi truyền dữ liệu đầu vào.
Nguyên nhân | Điều sẽ xảy ra | Cách khắc phục được đề xuất |
---|---|---|
Lệnh gọi liên kết bị chậm | Luồng chính thực hiện một lệnh gọi liên kết đồng bộ diễn ra trong thời gian dài. | Di chuyển lệnh gọi đó ra khỏi luồng chính hoặc cố gắng tối ưu hoá lệnh gọi nếu bạn sở hữu API. |
Nhiều lệnh gọi liên kết liên tiếp | Luồng chính thực hiện nhiều lệnh gọi liên kết đồng bộ liên tiếp. | Không thực hiện các lệnh gọi liên kết trong một chu kỳ tối giản. |
Chặn I/O | Luồng chính giúp chặn lệnh gọi I/O, chẳng hạn như quyền truy cập cơ sở dữ liệu hoặc mạng. | Di chuyển tất cả lệnh chặn I/O ra khỏi luồng chính. |
Tranh chấp khoá | Luồng chính bị chặn đang chờ lấy một khoá. | Giảm tranh chấp khoá giữa luồng chính và luồng khác. Tối ưu hoá mã có tốc độ chậm trong luồng khác. |
Khung tốn nhiều tài nguyên | Kết xuất quá nhiều trong một khung hình, gây ra tình trạng giật nghiêm trọng. | Giảm bớt thao tác khi kết xuất khung. Không sử dụng thuật toán n2. Sử dụng các thành phần hiệu quả cho những tính năng như cuộn hoặc phân trang – ví dụ: Thư viện Jetpack Paging. |
Bị thành phần khác chặn | Một thành phần khác (chẳng hạn như broadcast receiver) đang chạy chặn luồng chính. | Di chuyển thao tác không liên quan đến giao diện người dùng ra khỏi luồng chính càng nhiều càng tốt. Chạy broadcast receiver trên một luồng khác. |
GPU bị treo | GPU bị treo là một sự cố xảy ra với hệ thống hoặc phần cứng khiến quá trình kết xuất bị chặn, dẫn đến lỗi ANR khi truyền dữ liệu đầu vào. | Rất tiếc, về phía ứng dụng, thường không có bất kỳ cách khắc phục nào. Nếu có thể, hãy liên hệ với nhóm phụ trách phần cứng để khắc phục sự cố. |
Cách gỡ lỗi
Hãy bắt đầu gỡ lỗi bằng cách xem chữ ký cụm lỗi ANR trong Google Play Console hoặc Firebase Crashlytics. Cụm này thường chứa các khung hình ở trên cùng bị nghi là nguyên nhân gây ra lỗi ANR.
Biểu đồ quy trình sau đây cho biết cách xác định nguyên nhân gây ra lỗi ANR do hết thời gian chờ truyền dữ liệu đầu vào.
Play vitals có thể phát hiện và giúp giải quyết một số nguyên nhân thường gặp gây ra lỗi ANR. Ví dụ: nếu phát hiện thấy một lỗi ANR xảy ra do tranh chấp khoá, thì vitals có thể tóm tắt sự cố và đề xuất cách khắc phục trong phần Thông tin chi tiết về lỗi ANR.
Không có cửa sổ nào được lấy tiêu điểm
Mặc dù các sự kiện chẳng hạn như chạm được gửi trực tiếp đến cửa sổ liên quan dựa trên thử nghiệm nhấn, nhưng các sự kiện chẳng hạn như khoá lại cần có một mục tiêu. Mục tiêu này được gọi là cửa sổ được lấy tiêu điểm. Mỗi màn hình chỉ có một cửa sổ được lấy tiêu điểm và thường là cửa sổ mà người dùng đang tương tác. Nếu không tìm thấy cửa sổ được lấy tiêu điểm, thao tác đầu vào sẽ gây ra lỗi ANR do không có cửa sổ được lấy tiêu điểm. Lỗi ANR do không có cửa sổ được lấy tiêu điểm là một loại lỗi ANR khi truyền dữ liệu đầu vào.
Khoảng thời gian chờ mặc định: 5 giây.
Nguyên nhân thường gặp
Lỗi ANR do không có cửa sổ được lấy tiêu điểm thường do một trong những vấn đề sau gây ra:
- Ứng dụng đang thực hiện quá nhiều hoạt động và có tốc độ quá chậm nên không vẽ được khung đầu tiên.
- Không thể lấy cửa sổ chính làm tiêu điểm. Nếu một cửa sổ được gắn cờ bằng
FLAG_NOT_FOCUSABLE
, thì người dùng sẽ không thể gửi các sự kiện liên quan tới nút hoặc phím đến cửa sổ đó.
Kotlin
override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) window.addFlags(WindowManager.LayoutParams.FLAG_FLAG_NOT_FOCUSABLE) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); }
Hết thời gian chờ broadcast receiver
Lỗi ANR liên quan đến broadcast receiver xảy ra khi một broadcast receiver không xử lý thông báo truyền tin kịp thời. Đối với các receiver đồng bộ hoặc các receiver không gọi goAsync()
, hết thời gian chờ có nghĩa là onReceive()
không hoàn tất đúng thời gian. Đối với các receiver không đồng bộ hoặc các receiver gọi goAsync()
, hết thời gian chờ có nghĩa là PendingResult.finish()
không được gọi kịp thời.
Lỗi ANR liên quan đến broadcast receiver thường xảy ra trong các luồng sau:
- Luồng chính, nếu vấn đề là ứng dụng khởi động chậm.
- Luồng đang chạy broadcast receiver nếu vấn đề là mã
onReceive()
có tốc độ chậm. - Các luồng worker truyền tin, nếu vấn đề là mã truyền tin
goAsync()
có tốc độ chậm.
Để tránh các lỗi ANR liên quan đến broadcast receiver, hãy làm theo các phương pháp hay nhất sau:
- Đảm bảo ứng dụng khởi động nhanh vì thời gian này được tính vào thời gian chờ ANR nếu ứng dụng bắt đầu xử lý thông báo truyền tin.
- Nếu dùng
goAsync()
, hãy nhớ gọiPendingResult.finish()
thật nhanh. Mã này có thời gian chờ ANR tương tự như các broadcast receiver đồng bộ. - Nếu dùng
goAsync()
, hãy đảm bảo (các) luồng worker không được chia sẻ với các thao tác chặn hoặc hoạt động khác diễn ra trong thời gian dài. - Cân nhắc sử dụng
registerReceiver()
để chạy broadcast receiver trong một luồng không phải luồng chính, nhằm tránh chặn mã giao diện người dùng chạy trong luồng chính.
Khoảng thời gian chờ
Khoảng thời gian chờ broadcast receiver phụ thuộc vào việc cờ ý định trên nền trước có được đặt hay không và phiên bản nền tảng.
Loại ý định | Android 13 trở xuống | Android 14 trở lên |
---|---|---|
Ý định ưu tiên khi ở nền trước ( |
10 giây |
10 – 20 giây, tuỳ thuộc vào việc xử lý có bị thiếu CPU hay không |
Ý định ưu tiên khi ở nền sau ( |
60 giây |
60 – 120 giây, tuỳ thuộc vào việc xử lý có bị thiếu CPU hay không |
Để biết cờ FLAG_RECEIVER_FOREGROUND
có được đặt hay không, hãy tìm "flg=" trong tiêu đề ANR và xem có 0x10000000
hay không. Nếu bit này được đặt thì ý định đã được đặt FLAG_RECEIVER_FOREGROUND
, do đó, thời gian chờ sẽ ngắn hơn.
Dưới đây là ví dụ về tiêu đề ANR có thời gian chờ truyền tin ngắn (10 – 20 giây):
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
Dưới đây là ví dụ về tiêu đề ANR có thời gian chờ truyền tin dài (60 – 120 giây):
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
Cách đo thời gian truyền tin
Quá trình đo lường thời lượng truyền tin bắt đầu khi thông báo truyền tin được gửi từ system_server
đến ứng dụng và kết thúc khi ứng dụng xử lý xong thông báo truyền tin đó. Nếu quy trình xử lý ứng dụng chưa chạy, thì quy trình này cũng cần khởi động nguội trong khoảng thời gian chờ ANR. Do đó, việc ứng dụng khởi động chậm có thể dẫn đến lỗi ANR liên quan đến broadcast receiver.
Hình sau đây minh hoạ tiến trình ANR liên quan đến broadcast receiver tương ứng với một số quy trình của ứng dụng.
Quá trình đo lường thời gian chờ ANR kết thúc khi receiver xử lý xong thông báo truyền tin: thời điểm chính xác diễn ra việc này phụ thuộc vào việc receiver đó là receiver đồng bộ hay không đồng bộ.
- Đối với các receiver đồng bộ, quá trình đo lường sẽ dừng khi trả về
onReceive()
. - Đối với các receiver không đồng bộ, quá trình đo lường sẽ dừng khi
PendingResult.finish()
được gọi.
Nguyên nhân thường gặp
Dưới đây là một số nguyên nhân thường gặp và cách khắc phục được đề xuất cho lỗi ANR liên quan đến broadcast receiver.
Nguyên nhân | Áp dụng cho | Điều đã xảy ra | Cách khắc phục được đề xuất |
---|---|---|---|
Ứng dụng khởi động chậm | Tất cả các receiver | Ứng dụng mất quá nhiều thời gian để khởi động nguội. | Tối ưu hoá quá trình khởi động cho ứng dụng bị khởi động chậm. |
onReceive() chưa lên lịch | Tất cả các receiver | Luồng broadcast receiver bận thực hiện công việc khác và không thể bắt đầu phương thức onReceive() . | Không thực hiện các tác vụ chạy trong thời gian dài trên luồng của receiver (hoặc di chuyển receiver sang luồng chuyên dụng). |
onReceive() có tốc độ chậm | Tất cả các receiver, nhưng chủ yếu là các receiver đồng bộ | Phương thức onReceive() đã bắt đầu nhưng bị chặn hoặc có tốc độ chậm nên không hoàn thành đúng thời gian. | Tối ưu hoá mã receiver có tốc độ chậm. |
Các tác vụ của receiver không đồng bộ chưa được lên lịch | Các receiver goAsync() | Phương thức onReceive() đã cố thực thi thao tác trên một nhóm luồng worker bị chặn, vì vậy, thao tác sẽ không bao giờ bắt đầu. |
Tối ưu hoá các lệnh gọi bị chậm hoặc chặn hoặc sử dụng luồng cho worker truyền tin khác với luồng cho các tác vụ khác chạy trong thời gian dài. |
Worker bị chậm hoặc bị chặn | Các receiver goAsync() |
Có một thao tác chặn hoặc thao tác bị chậm ở đâu đó trong nhóm luồng worker khi xử lý thông báo truyền tin. Vì vậy, PendingResult.finish không được gọi kịp thời. | Tối ưu hoá mã receiver async bị chậm. |
Quên gọi PendingResult.finish |
Các receiver goAsync() |
Đường dẫn mã thiếu lệnh gọi đến finish() . |
Đảm bảo finish() luôn được gọi. |
Cách gỡ lỗi
Dựa vào báo cáo ANR và chữ ký cụm, bạn có thể xác định luồng mà receiver chạy trên đó cũng như mã cụ thể bị thiếu hoặc chạy chậm.
Biểu đồ quy trình sau cho biết cách xác định nguyên nhân gây ra lỗi ANR liên quan đến broadcast receiver.
Tìm mã receiver
Google Play Console hiện lớp receiver và ý định truyền tin trong chữ ký ANR. Hãy tìm các mã sau:
cmp=<receiver class>
act=<broadcast_intent>
Dưới đây là ví dụ về một chữ ký ANR liên quan đến broadcast receiver:
com.example.app.MyClass.myMethod
Broadcast of Intent { act=android.accounts.LOGIN_ACCOUNTS_CHANGED
cmp=com.example.app/com.example.app.MyAccountReceiver }
Tìm luồng chạy phương thức onReceive()
Nếu bạn đang sử dụng Context.registerReceiver
để chỉ định một trình xử lý tuỳ chỉnh, thì đó chính là luồng đang chạy trình xử lý này. Còn không, thì đó là luồng chính.
Ví dụ: các tác vụ của receiver không đồng bộ chưa được lên lịch
Phần này trình bày một ví dụ về cách gỡ lỗi ANR liên quan đến broadcast receiver.
Giả sử chữ ký ANR trông như sau:
com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }
Dựa vào chữ ký này, có vẻ như ý định truyền tin là android.accounts.LOG_ACCOUNTS_CHANGED
và lớp receiver là com.example.app.MyReceiver
.
Từ mã receiver, bạn có thể xác định rằng nhóm luồng "Luồng BG [0,1,2,3]" thực hiện thao tác chính để xử lý thông báo truyền tin này. Khi xem tệp kết xuất ngăn xếp, bạn có thể thấy rằng cả 4 luồng ở nền sau (BG) đều có cùng một mẫu: các luồng này chạy một lệnh gọi chặn, getDataSync
. Vì tất cả các luồng BG đều bận nên thông báo truyền tin không được xử lý kịp thời, dẫn đến một lỗi ANR.
BG Thread #0 (tid=26) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture:563)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture:68)
at com.example.app.getDataSync(<MyClass>:152)
...
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at com.google.android.libraries.concurrent.AndroidExecutorsModule.lambda$withStrictMode$5(AndroidExecutorsModule:451)
at com.google.android.libraries.concurrent.AndroidExecutorsModule$$ExternalSyntheticLambda8.run(AndroidExecutorsModule:1)
at java.lang.Thread.run(Thread.java:1012)
at com.google.android.libraries.concurrent.ManagedPriorityThread.run(ManagedPriorityThread:34)
There are several approaches to fix the issue:
- Find out why
getDataSync
is slow and optimize. - Don't run
getDataSync
on all four BG threads. - More generally, ensure that the BG thread pool isn't saturated with long-running operations.
- Use a dedicated thread pool for
goAsync
worker tasks. - Use an unbounded thread pool instead of the bounded BG thread pool
Example: slow app startup
A slow app startup can cause several types of ANRs, especially broadcast
receiver and execute service ANRs. The cause of an
ANR is likely slow app startup if you see ActivityThread.handleBindApplication
in the main thread stacks.
Execute service timeout
An execute service ANR happens when the app's main thread doesn't start a
service in time. Specifically, a service doesn't finish executing
onCreate()
and onStartCommand()
or onBind()
within the
timeout period.
Default timeout period: 20 seconds for foreground service; 200 seconds for
background service. The ANR timeout period includes the app cold start, if
necessary, and calls to onCreate(), onBind()
, or onStartCommand()
.
To avoid execute service ANRs, follow these general best practices:
- Make sure that app startup is fast, since it's counted in the ANR timeout if the app is started to run the service component.
- Make sure that the service's
onCreate()
,onStartCommand()
, andonBind()
methods are fast. - Avoid running any slow or blocking operations on the main thread from other components; these operations can prevent a service from starting quickly.
Common causes
The following table lists common causes of execute service ANRs and suggested fixes.
Cause | What | Suggested fix |
---|---|---|
Slow app startup | The app takes too long to perform a cold start. | Optimize slow app start. |
Slow onCreate(), onStartCommand (), or
onBind() |
The service component's onCreate(),
onStartCommand (), or onBind() method takes too long to
execute on the main thread. |
Optimize slow code. Move slow operations off the critical path where possible. |
Not scheduled (main thread blocked before onStart() ) |
The app's main thread is blocked by another component before the service can be started. | Move other component's work off the main thread. Optimize other component's blocking code. |
How to debug
From the cluster signature and ANR report in Google Play Console or Firebase Crashlytics, you can often determine the cause of the ANR based on what the main thread is doing.
The following flow chart describes how to debug an execute service ANR.
If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:
Find the service component class in the ANR signature. In Google Play Console, the service component class is shown in the ANR signature. In the following example ANR details, it's
com.example.app/MyService
.com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly Executing service com.example.app/com.example.app.MyService
Determine whether the slow or block operation is part of app startup, the service component, or elsewhere by checking for the following important function call(s) in the main threads.
Function call(s) in main thread stacks What it means android.app.ActivityThread.handleBindApplication
App was starting up, so the ANR was caused by slow app start. <ServiceClass>.onCreate()
[...]
android.app.ActivityThread.handleCreateService
Service was being created, so the ANR was likely caused by slow onCreate()
code.<ServiceClass>.onBind()
[...]
android.app.ActivityThread.handleBindService
Service was being bound, so the ANR was likely caused by slow onBind()
code.<ServiceClass>.onStartCommand()
[...]
android.app.ActivityThread.handleServiceArgs
Service was being started, so the ANR was likely caused by slow onStartCommand()
code.For example, if the
onStartCommand()
method in theMyService
class is slow, the main threads will look like this:at com.example.app.MyService.onStartCommand(FooService.java:25) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4820) at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(unavailable:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2289) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:205) at android.os.Looper.loop(Looper.java:294) at android.app.ActivityThread.main(ActivityThread.java:8176) at java.lang.reflect.Method.invoke(Native method:0)
Nếu bạn không thấy bất kỳ lệnh gọi hàm quan trọng nào, thì dưới đây là một số khả năng khác:
- Dịch vụ đang chạy hoặc ngừng hoạt động, tức là các ngăn xếp được lấy quá muộn. Trong trường hợp này, bạn có thể bỏ qua lỗi ANR vì là dạng lỗi dương tính giả.
- Một thành phần ứng dụng khác đang chạy, chẳng hạn như broadcast receiver. Trong trường hợp như vậy, luồng chính có thể bị chặn trong thành phần này, khiến dịch vụ không bắt đầu được.
Nếu bạn thấy một lệnh gọi hàm chính và có thể xác định vị trí thường xảy ra lỗi ANR, hãy kiểm tra các ngăn xếp luồng chính còn lại để tìm thao tác có tốc độ chậm và tiến hành tối ưu hoá hoặc chuyển thao tác đó ra khỏi quá trình quan trọng.
- Đảm bảo ứng dụng khởi động nhanh vì thời gian này được tính vào thời gian chờ ANR nếu ứng dụng bắt đầu chạy trình cung cấp nội dung.
- Đảm bảo rằng các truy vấn của trình cung cấp nội dung có tốc độ nhanh.
- Không thực hiện nhiều lệnh gọi liên kết chặn đồng thời có khả năng chặn tất cả các luồng liên kết của ứng dụng.
- Tệp kết xuất ngăn xếp muộn. Luồng đã khôi phục trong khoảng thời gian ngắn giữa thời điểm kích hoạt lỗi ANR và thời điểm ngăn xếp được kết xuất. Độ trễ trong Pixel trên Android 13 là khoảng 100 mili giây, nhưng có thể vượt quá 1 giây. Độ trễ trong Pixel trên Android 14 thường dưới 10 mili giây.
- Phân bổ sai luồng. Luồng dùng để tạo chữ ký ANR không phải là luồng không phản hồi thực sự gây ra lỗi ANR. Trong trường hợp này, hãy cố gắng xác định xem lỗi ANR có thuộc một trong các loại sau đây hay không:
- Sự cố trên toàn hệ thống. Quá trình này không được lên lịch do hệ thống quá tải hoặc xảy ra sự cố ở máy chủ hệ thống.
- Việc lấy ngăn xếp mất quá nhiều thời gian và hết thời gian chờ.
- Quá trình bị gián đoạn hoặc bị dừng trước khi các ngăn xếp được lấy.
Để biết thêm thông tin về các dịch vụ, hãy xem các trang sau:
Trình cung cấp nội dung không phản hồi
Lỗi ANR của trình cung cấp nội dung xảy ra khi một trình cung cấp nội dung từ xa mất nhiều thời gian hơn so với khoảng thời gian chờ để phản hồi một truy vấn và bị dừng.
Khoảng thời gian chờ mặc định: do trình cung cấp nội dung chỉ định bằng cách sử dụng ContentProviderClient.setDetectNotResponding
. Khoảng thời gian chờ ANR bao gồm tổng thời gian cần để chạy một truy vấn của trình cung cấp nội dung từ xa, kể cả thời gian khởi động nguội ứng dụng từ xa nếu ứng dụng chưa chạy.
Để tránh lỗi ANR của trình cung cấp nội dung, hãy làm theo các phương pháp hay nhất sau:
Nguyên nhân thường gặp
Bảng sau đây liệt kê các nguyên nhân thường gặp gây ra lỗi ANR của trình cung cấp nội dung và cách khắc phục được đề xuất.
Nguyên nhân | Điều sẽ xảy ra | Tín hiệu | Cách khắc phục được đề xuất |
---|---|---|---|
Truy vấn của trình cung cấp nội dung có tốc độ chậm | Trình cung cấp nội dung mất quá nhiều thời gian để thực thi hoặc bị chặn. | Khung android.content.ContentProvider$Transport.query nằm trong luồng liên kết. |
Tối ưu hoá truy vấn của trình cung cấp nội dung. Tìm hiểu điều đang chặn luồng liên kết. |
Ứng dụng khởi động chậm | Ứng dụng của trình cung cấp nội dung mất quá nhiều thời gian để khởi động. | Khung ActivityThread.handleBindApplication nằm trong luồng chính. |
Tối ưu hoá quá trình khởi động ứng dụng. |
Tình trạng cạn kiệt luồng liên kết – tất cả các luồng liên kết đều đang bận | Tất cả các luồng liên kết đều đang bận phân phát các yêu cầu đồng bộ khác nên không thể chạy lệnh gọi liên kết của trình cung cấp nội dung. | Ứng dụng không khởi động, tất cả các luồng liên kết đều bận và trình cung cấp nội dung hiện không chạy. | Giảm tải cho các luồng liên kết. Điều này nghĩa là giảm bớt các lệnh gọi liên kết đi đồng bộ hoặc thực hiện ít thao tác hơn khi xử lý các cuộc gọi đến. |
Cách gỡ lỗi
Để gỡ lỗi lỗi ANR của trình cung cấp nội dung bằng chữ ký cụm và báo cáo ANR trong Google Play Console hoặc Firebase Crashlytics, hãy xem hoạt động của luồng chính và (các) luồng liên kết.
Biểu đồ sau đây mô tả cách gỡ lỗi ANR của trình cung cấp nội dung:
Đoạn mã sau đây cho thấy giao diện của luồng liên kết khi bị chặn do truy vấn của trình cung cấp nội dung có tốc độ chậm. Trong trường hợp này, truy vấn của trình cung cấp nội dung sẽ chờ khoá khi mở một cơ sở dữ liệu.
binder:11300_2 (tid=13) Blocked
Waiting for osm (0x01ab5df9) held by at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers:182)
at com.example.app.MyClass.blockingGetOpenDatabase(FooClass:171)
[...]
at com.example.app.MyContentProvider.query(MyContentProvider.java:915)
at android.content.ContentProvider$Transport.query(ContentProvider.java:292)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:107)
at android.os.Binder.execTransactInternal(Binder.java:1339)
at android.os.Binder.execTransact(Binder.java:1275)
Đoạn mã sau đây cho thấy giao diện của luồng chính khi bị chặn do ứng dụng khởi động chậm. Trong trường hợp này, quá trình khởi động ứng dụng bị chậm do tranh chấp khoá trong quá trình khởi chạy Dagger.
main (tid=1) Blocked
[...]
at dagger.internal.DoubleCheck.get(DoubleCheck:51)
- locked 0x0e33cd2c (a qsn)at dagger.internal.SetFactory.get(SetFactory:126)
at com.myapp.Bar_Factory.get(Bar_Factory:38)
[...]
at com.example.app.MyApplication.onCreate(DocsApplication:203)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6991)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2235)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8170)
at java.lang.reflect.Method.invoke(Native method:0)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Chậm phản hồi lệnh
ANR phản hồi chậm với lệnh khi ứng dụng mất quá nhiều thời gian phản hồi JobService.onStartJob()
hoặc JobService.onStopJob()
hoặc mất quá nhiều thời gian để đưa ra một thông báo bằng JobService.setNotification()
. Điều này cho thấy luồng chính của ứng dụng bị chặn khi thực hiện một thao tác nào đó khác.
Nếu đó là vấn đề xảy ra với JobService.onStartJob()
hoặc JobService.onStopJob()
, hãy kiểm tra những gì đang diễn ra trên luồng chính. Nếu đây là vấn đề xảy ra với JobService.setNotification()
, hãy nhớ gọi lệnh này nhanh nhất có thể.
Không thực hiện nhiều thao tác trước khi đưa ra thông báo.
Lỗi ANR bí ẩn
Đôi khi, bạn không rõ lý do xảy ra lỗi ANR hoặc không có đủ thông tin trong chữ ký cụm và báo cáo ANR để gỡ lỗi này. Trong những trường hợp như vậy, bạn vẫn có thể thực hiện một số bước để xác định xem lỗi ANR có xử lý được hay không.
Hàng đợi tin nhắn ở trạng thái rảnh hoặc nativePollOnce
Nếu bạn thấy khung android.os.MessageQueue.nativePollOnce
trong ngăn xếp, thì điều này thường cho biết rằng luồng bị nghi ngờ không phản hồi thực sự ở trạng thái rảnh và đang chờ các thông báo của trình lặp. Trong Google Play Console, thông tin chi tiết về lỗi ANR sẽ có dạng như sau:
Native method - android.os.MessageQueue.nativePollOnce
Executing service com.example.app/com.example.app.MyService
Ví dụ: nếu luồng chính đang ở trạng thái rảnh, các ngăn xếp sẽ có dạng như sau:
"main" tid=1 NativeMain threadIdle
#00 pc 0x00000000000d8b38 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
#01 pc 0x0000000000019d88 /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
#02 pc 0x0000000000019c68 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
#03 pc 0x000000000011409c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (Native method)
at android.os.MessageQueue.next (MessageQueue.java:339) at android.os.Looper.loop (Looper.java:208)
at android.app.ActivityThread.main (ActivityThread.java:8192)
at java.lang.reflect.Method.invoke (Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:626)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1015)
Dưới đây là một số lý do khiến luồng bị nghi ngờ không phản hồi có thể ở trạng thái rảnh:
Không có khung ngăn xếp
Một số báo cáo ANR không chứa các ngăn xếp có lỗi ANR, nghĩa là việc kết xuất ngăn xếp không thành công khi tạo báo cáo ANR. Dưới đây là một số lý do có thể dẫn đến thiếu khung ngăn xếp:
[...]
--- CriticalEventLog ---
capacity: 20
timestamp_ms: 1666030897753
window_ms: 300000
libdebuggerd_client: failed to read status response from tombstoned: timeout reached?
----- Waiting Channels: pid 7068 at 2022-10-18 02:21:37.<US_SOCIAL_SECURITY_NUMBER>+0800 -----
[...]
Bạn sẽ không thể dùng báo cáo lỗi ANR hoặc chữ ký cụm để xử lý lỗi ANR do không có khung ngăn xếp. Để gỡ lỗi, hãy xem xét các cụm khác của ứng dụng, vì nếu một vấn đề đủ lớn, ứng dụng thường sẽ có cụm riêng chứa khung ngăn xếp. Hoặc bạn có thể xem dấu vết Perfetto.
Vấn đề đã biết
Việc giữ bộ tính giờ trong quy trình của ứng dụng nhằm hoàn tất quá trình xử lý thông báo truyền tin trước khi một điều kiện kích hoạt lỗi ANR có thể không hoạt động chính xác do hệ thống theo dõi các lỗi ANR theo cách không đồng bộ.