Khắc phục sự cố về dịch vụ trên nền trước

Trang này thảo luận về một số lý do phổ biến khiến dịch vụ trên nền trước có thể gặp lỗi và giúp bạn xác định nguyên nhân gây ra vấn đề.

Tài liệu này thảo luận về các vấn đề sau:

Trước khi bạn khắc phục sự cố

Kiểm tra các thay đổi gần đây đối với dịch vụ trên nền trước

Nếu được sử dụng không đúng cách, dịch vụ trên nền trước có thể gây ảnh hưởng tiêu cực đến hiệu suất của thiết bị và thời lượng pin. Vì lý do này, các bản phát hành nền tảng Android thường có những thay đổi đối với hành vi của dịch vụ trên nền trước để hạn chế những ảnh hưởng xấu này.

Nếu bạn gặp vấn đề với dịch vụ trên nền trước, bạn nên xem tài liệu về các thay đổi đối với dịch vụ trên nền trước và xem liệu có thay đổi nào gần đây có thể giải thích cho vấn đề của bạn hay không. Bạn đặc biệt cần kiểm tra các thay đổi trong những trường hợp sau:

  • Mã dịch vụ trên nền trước từng hoạt động nhưng hiện gặp lỗi
  • Bạn vừa bắt đầu kiểm thử trên một bản phát hành nền tảng mới hoặc bạn đã thay đổi cấp độ API mà ứng dụng của bạn nhắm đến

Ngoài ra, nếu bạn đang kiểm thử thiết bị của mình trên bản dùng thử dành cho nhà phát triển của nền tảng, hãy nhớ kiểm tra phiên bản mới nhất của tài liệu về bản dùng thử dành cho nhà phát triển.

Lỗi ứng dụng không phản hồi (ANR)

Trong một số trường hợp, ứng dụng dự kiến sẽ tắt dịch vụ trên nền trước. Nếu ứng dụng không dừng dịch vụ, hệ thống sẽ dừng dịch vụ và kích hoạt lỗi Ứng dụng không phản hồi (ANR).

Dịch vụ ngắn chạy quá lâu gây ra lỗi ANR

Các dịch vụ trên nền trước sử dụng loại dịch vụ ngắn phải hoàn tất nhanh chóng, trong vòng khoảng 3 phút. Khi hết thời gian, hệ thống sẽ gọi phương thức của dịch vụ.Service.onTimeout(int,int) Dịch vụ có vài giây để gọi stopSelf(). Nếu dịch vụ không tự dừng, hệ thống sẽ kích hoạt lỗi Ứng dụng không phản hồi.

Chẩn đoán:

Nếu lỗi ANR là do dịch vụ trên nền trước không tự dừng, hệ thống sẽ gửi một ngoại lệ nội bộ. Bạn có thể xác minh rằng đây là vấn đề bằng cách kiểm tra báo cáo ANR. Nếu đây là vấn đề, báo cáo sẽ có thông báo sau:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type FOREGROUND_SERVICE_TYPE_SHORT_SERVICE did not stop within its timeout:
[component name]"

Cách khắc phục:

Đảm bảo rằng tất cả dịch vụ trên nền trước có giới hạn về thời gian đều hoàn tất công việc và gọi stopForeground(int) trong giới hạn thời gian của hệ thống.

Yêu cầu dịch vụ trên nền trước của bạn triển khai Service.onTimeout(int,int). Đảm bảo rằng quá trình triển khai phương thức đó gọi stopSelf() ngay lập tức.

Ngoại lệ của dịch vụ trên nền trước

Phần này mô tả một số vấn đề về dịch vụ trên nền trước có thể khiến hệ thống gửi một ngoại lệ. Nếu ứng dụng không bắt được ngoại lệ, người dùng sẽ thấy một hộp thoại cho biết ứng dụng đã dừng.

Trong một số trường hợp, hệ thống sẽ gửi một ngoại lệ nội bộ. Trong những trường hợp đó, bạn có thể tìm hiểu ngoại lệ là gì bằng cách xem trong dấu vết ngăn xếp, và bạn có thể kiểm tra Logcat để biết thông tin chi tiết hơn về lỗi.

Ngoại lệ nội bộ: Đã quá thời gian chờ

Hệ thống áp đặt giới hạn về thời gian chạy của dịch vụ trên nền trước để đồng bộ hoá dữ liệu và xử lý nội dung nghe nhìn khi ứng dụng ở chế độ nền. Nếu dịch vụ vượt quá giới hạn đó, hệ thống sẽ gọi phương thức Service.onTimeout(int,int) của dịch vụ. Dịch vụ có vài giây để gọi stopSelf(). Nếu dịch vụ không tự dừng, hệ thống sẽ tạo một RemoteServiceException nội bộ khiến ứng dụng gặp sự cố.

Chẩn đoán:

Bạn có thể tìm hiểu ngoại lệ là gì bằng cách xem trong dấu vết ngăn xếp và bạn có thể kiểm tra Logcat để biết thông tin chi tiết hơn về lỗi. Trong trường hợp này, Logcat có thông báo lỗi sau:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type [service type] did not stop within its timeout: [component name]"

Cách khắc phục:

Đảm bảo rằng tất cả dịch vụ trên nền trước có giới hạn về thời gian đều hoàn tất công việc và gọi stopForeground(int) trong giới hạn thời gian của hệ thống.

Yêu cầu dịch vụ trên nền trước của bạn triển khai Service.onTimeout(int,int). Đảm bảo rằng quá trình triển khai phương thức đó gọi stopSelf() ngay lập tức.

Ngoại lệ nội bộ: ForegroundServiceDidNotStartInTimeException

Khi bạn khởi chạy một dịch vụ bằng cách gọi context.startForegroundService(), dịch vụ đó có vài giây để tự nâng cấp lên dịch vụ trên nền trước bằng cách gọi ServiceCompat.startForeground(). Nếu dịch vụ không làm như vậy, sẽ gửi một ForegroundServiceDidNotStartInTimeException nội bộ.

Chẩn đoán:

Bạn có thể tìm hiểu ngoại lệ là gì bằng cách xem trong dấu vết ngăn xếp và bạn có thể kiểm tra Logcat để biết thông tin chi tiết hơn về lỗi. Trong trường hợp này, Logcat có thông báo lỗi sau:

android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException:
Context.startForegroundService() did not then call Service.startForeground()

Cách khắc phục:

Đảm bảo rằng tất cả dịch vụ trên nền trước mới tạo đều gọi ServiceCompat.startForeground() trong vòng vài giây.

WorkManager:

Bạn cũng có thể thấy ngoại lệ này với các worker WorkManager thực thi một dịch vụ trên nền trước (gọi setForegound hoặc setForegroundAsync). Khi vòng đời của 2 worker trên nền trước trùng lặp khi một worker cố gắng khởi động một dịch vụ trên nền trước trong khi một dịch vụ trên nền trước đang chạy trước đó đang cố gắng tắt, sự cố này sẽ đi kèm với nhật ký sau:

Re-initializing SystemForegroundService after a request to shut-down

Bản sửa lỗi cho sự cố này đã được giới thiệu trong WorkManager phiên bản 2.10.5.

Nếu ứng dụng của bạn gặp phải ngoại lệ này, hãy cập nhật lên phiên bản WorkManager mới nhất và báo cáo mọi vấn đề còn tồn tại cho trình theo dõi vấn đề của WorkManager.

ForegroundServiceStartNotAllowedException

Lỗi:

Hệ thống gửi ForegroundServiceStartNotAllowedException.

Nguyên nhân:

Nguyên nhân thường là do ứng dụng khởi chạy một dịch vụ trên nền trước từ nền khi không có trường hợp miễn trừ hợp lệ.

Kể từ Android 12 (cấp độ API 31), các ứng dụng không được phép khởi động dịch vụ trên nền trước khi ứng dụng đang chạy ở chế độ nền, với một số trường hợp miễn trừ cụ thể. Nếu bạn cố gắng khởi động một dịch vụ trên nền trước từ nền và không đáp ứng các yêu cầu của một trong các trường hợp miễn trừ, hệ thống sẽ gửi ForegroundServiceStartNotAllowedException. Hệ thống cũng làm như vậy nếu bạn không đáp ứng các yêu cầu của trường hợp miễn trừ.

Ví dụ: một ứng dụng có thể có một nút mà người dùng có thể nhấp vào, khiến ứng dụng thực hiện một số hoạt động xử lý rồi khởi chạy một dịch vụ trên nền trước. Trong trường hợp này, có nguy cơ người dùng có thể nhấp vào nút rồi ngay lập tức chuyển ứng dụng sang chế độ nền. Sau đó, ứng dụng sẽ cố gắng khởi chạy dịch vụ từ nền. Nếu ứng dụng không đáp ứng một trong các trường hợp miễn trừ được chỉ định, hệ thống sẽ gửi ForegroundServiceStartNotAllowedException.

Ngoài ra, một số trường hợp miễn trừ có giới hạn thời gian ngắn. Ví dụ: có một trường hợp miễn trừ ngắn nếu ứng dụng của bạn khởi chạy một dịch vụ trên nền trước để phản hồi một thông báo FCM có mức độ ưu tiên cao. Nếu bạn không khởi chạy dịch vụ đủ nhanh, bạn sẽ nhận được ForegroundServiceStartNotAllowedException.

Đôi khi, các trường hợp miễn trừ cụ thể trở nên hạn chế hơn với các bản phát hành Android mới. Nếu bạn đã thay đổi phiên bản Android mà ứng dụng của bạn nhắm đến, hãy xem tài liệu về các thay đổi đối với dịch vụ trên nền trước và xác nhận rằng ứng dụng của bạn vẫn đáp ứng một trong các trường hợp miễn trừ được phép.

Cách khắc phục:

Thay đổi quy trình làm việc của ứng dụng để ứng dụng không cần khởi chạy dịch vụ trên nền trước khi ứng dụng ở chế độ nền hoặc xác nhận rằng ứng dụng của bạn đáp ứng một trong các trường hợp miễn trừ.

Bạn có thể sử dụng các thành phần nhận biết được vòng đời để quản lý vòng đời của ứng dụng để không vô tình cố gắng khởi chạy một dịch vụ trên nền trước từ nền.

SecurityException

Lỗi:

Hệ thống gửiSecurityException.

Nguyên nhân:

Ứng dụng của bạn đã cố gắng khởi chạy một dịch vụ trên nền trước mà không có các quyền cần thiết.

  • Nếu một ứng dụng nhắm đến Android 9 (cấp độ API 28) trở lên, thì ứng dụng đó phải có quyền FOREGROUND_SERVICE để khởi chạy một dịch vụ trên nền trước.
  • Nếu một ứng dụng nhắm đến Android 14 (cấp độ API 34) trở lên, thì ứng dụng đó phải đáp ứng tất cả các điều kiện tiên quyết cho loại dịch vụ trên nền trước. Các điều kiện tiên quyết này được trình bày chi tiết trong tài liệu về các loại dịch vụ trên nền trước documentation. Cụ thể, hãy lưu ý các yêu cầu sau:
    • Một số loại dịch vụ trên nền trước yêu cầu các quyền cụ thể trong thời gian chạy. Ví dụ: dịch vụ trên nền trước để nhắn tin từ xa phải có quyền FOREGROUND_SERVICE_REMOTE_MESSAGING.
  • Trong một số trường hợp, có các hạn chế bổ sung trong khi sử dụng đối với các quyền mà một số loại dịch vụ trên nền trước cần. Các quyền này chỉ được cấp cho ứng dụng khi ứng dụng ở nền trước (với một số trường hợp ngoại lệ cụ thể). Điều này có nghĩa là ngay cả khi ứng dụng của bạn đã yêu cầu và được cấp một trong các quyền này, nếu ứng dụng cố gắng khởi chạy dịch vụ trên nền trước khi ứng dụng ở chế độ nền, hệ thống sẽ gửi SecurityException ngay cả khi ứng dụng có trường hợp miễn trừ để khởi động một dịch vụ trên nền trước từ nền. Để biết thêm thông tin, hãy xem bài viết Các hạn chế khi khởi động dịch vụ trên nền trước cần có quyền trong khi sử dụng permissions.
    • Bạn có thể nhận được SecurityException nếu bạn đã yêu cầu các quyền cần thiết nhưng bạn khởi động dịch vụ trên nền trước trước khi xác nhận rằng các quyền cần thiết đã được cấp.

Cách khắc phục:

Trước khi khởi chạy dịch vụ trên nền trước, hãy yêu cầu tất cả các quyền thích hợp cho dịch vụ trên nền trước và xác nhận rằng bạn đã đáp ứng tất cả các điều kiện tiên quyết khác trong thời gian chạy.