Chẩn đoán và khắc phục lỗi ANR

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.

Hình 1. Cách gỡ lỗi ANR khi 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.

Hình 2. Phát hiện lỗi ANR bằng Play vitals.

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 goAync(), 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ọi PendingResult.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

(FLAG_RECEIVER_FOREGROUND được đặt)

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

(FLAG_RECEIVER_FOREGROUND không được đặt)

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.

Hình 3. Tiến trình ANR liên quan đến broadcast receiver.

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.
Hình 4. Các điểm cuối đo lường thời gian chờ ANR cho các receiver đồng bộ và không đồng bộ.

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.

Hình 5. Cách gỡ 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)

Có một số phương pháp để khắc phục vấn đề này:

  • Tìm hiểu nguyên nhân getDataSync bị chậm và tiến hành tối ưu hoá.
  • Không chạy getDataSync trên cả 4 luồng BG.
  • Nhìn chung, hãy đảm bảo nhóm luồng BG không bị bão hoà với các thao tác diễn ra trong thời gian dài.
  • Sử dụng nhóm luồng chuyên dụng cho các tác vụ của worker goAsync.
  • Sử dụng nhóm luồng không bị ràng buộc thay vì nhóm luồng BG bị ràng buộc

Ví dụ: ứng dụng khởi động chậm

Việc ứng dụng khởi động chậm có thể gây ra nhiều loại lỗi ANR, đặc biệt là lỗi ANR khi thực thi dịch vụ và lỗi ANR liên quan đến broadcast receiver. Nguyên nhân gây ra lỗi ANR có thể là do ứng dụng khởi động chậm nếu bạn thấy ActivityThread.handleBindApplication trong ngăn xếp luồng chính.

Hết thời gian chờ thực thi dịch vụ

Lỗi ANR khi thực thi dịch vụ xảy ra khi luồng chính của ứng dụng không bắt đầu một dịch vụ kịp thời. Cụ thể, một dịch vụ chưa hoàn tất việc thực thi onCreate()onStartCommand() hoặc onBind() trong khoảng thời gian chờ.

Khoảng thời gian chờ mặc định: 20 giây đối với dịch vụ trên nền trước; 200 giây đối với dịch vụ nền. Khoảng thời gian chờ ANR bao gồm cả thời gian khởi động nguội ứng dụng (nếu cần) và các lệnh gọi đến onCreate(), onBind() hoặc onStartCommand().

Để tránh lỗi ANR khi thực thi dịch vụ, hãy làm theo các phương pháp chung hay nhất sau đây:

  • Đảm bảo rằng ứ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 thành phần dịch vụ.
  • Đảm bảo rằng các phương thức onCreate(), onStartCommand()onBind() của dịch vụ đều có tốc độ nhanh.
  • Tránh chạy bất kỳ thao tác nào bị chậm hoặc chặn trên luồng chính của các thành phần khác; những thao tác này có thể khiến dịch vụ không bắt đầu nhanh được.

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 dẫn đến lỗi ANR khi thực thi dịch vụ và các cách khắc phục được đề xuất.

Nguyên nhân Nội dung Cách khắc phục được đề xuất
Ứng dụng khởi động chậm Ứ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.
onCreate(), onStartCommand() hoặc onBind() có tốc độ chậm Phương thức onCreate(), onStartCommand() hoặc onBind() của thành phần dịch vụ mất quá nhiều thời gian để thực thi trên luồng chính. Tối ưu hoá mã có tốc độ chậm. Di chuyển các thao tác có tốc độ chậm ra khỏi quá trình quan trọng nếu có thể.
Chưa được lên lịch (luồng chính bị chặn trước khi onStart()) Luồng chính của ứng dụng bị một thành phần khác chặn trước khi dịch vụ có thể bắt đầu. Chuyển thao tác của thành phần khác ra khỏi luồng chính. Tối ưu hoá mã chặn của thành phần khác.

Cách gỡ lỗi

Từ báo cáo ANR và chữ ký cụm trong Google Play Console hoặc Firebase Crashlytics, bạn thường có thể xác định được nguyên nhân gây ra lỗi ANR dựa vào thao tác mà luồng chính đang thực hiện.

Biểu đồ quy trình sau mô tả cách gỡ một lỗi ANR khi thực thi dịch vụ.

Hình 6. Cách gỡ lỗi ANR khi thực thi dịch vụ.

Nếu bạn xác định rằng lỗi ANR khi thực thi dịch vụ là lỗi có thể xử lý, hãy làm theo các bước sau để giúp giải quyết vấn đề:

  1. Tìm lớp thành phần dịch vụ trong chữ ký ANR. Trong Google Play Console, lớp thành phần dịch vụ sẽ xuất hiện trong chữ ký ANR. Trong ví dụ sau đây về thông tin chi tiết ANR, đó là com.example.app/MyService.

    com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly
    Executing service com.example.app/com.example.app.MyService
    
  2. Xác định xem thao tác có tốc độ chậm hoặc thao tác chặn là một phần của quy trình khởi động ứng dụng, thành phần dịch vụ hay của quy trình khác bằng cách kiểm tra (các) lệnh gọi hàm quan trọng sau đây trong các luồng chính.

    (Các) lệnh gọi hàm trong ngăn xếp luồng chính Ý nghĩa
    android.app.ActivityThread.handleBindApplication Ứng dụng đang khởi động nên lỗi ANR là do ứng dụng khởi động chậm.

    <ServiceClass>.onCreate()

    [...]

    android.app.ActivityThread.handleCreateService

    Dịch vụ đang được tạo nên lỗi ANR có thể là do mã onCreate() có tốc độ chậm gây ra.

    <ServiceClass>.onBind()

    [...]

    android.app.ActivityThread.handleBindService

    Dịch vụ đang bị ràng buộc nên lỗi ANR có thể là do mã onBind() có tốc độ chậm gây ra.

    <ServiceClass>.onStartCommand()

    [...]

    android.app.ActivityThread.handleServiceArgs

    Dịch vụ đang được bắt đầu nên lỗi ANR có thể do mã onStartCommand() có tốc độ chậm gây ra.

    Ví dụ: nếu phương thức onStartCommand() trong lớp MyService có tốc độ chậm, thì các luồng chính sẽ có dạng như sau:

    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.
  3. 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.

Để 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:

  • Đả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.

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:

Hình 7. 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:

  • 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.

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:

  • 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.
[...]

--- 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ộ.