Giới hạn thực thi dưới nền

Mỗi khi chạy ở chế độ nền, một ứng dụng sẽ tiêu thụ một số tài nguyên có hạn của thiết bị, chẳng hạn như RAM. Điều này có thể dẫn đến trải nghiệm người dùng kém, đặc biệt là khi người dùng đang sử dụng một ứng dụng tốn nhiều tài nguyên, chẳng hạn như chơi trò chơi hoặc xem video. Để cải thiện trải nghiệm người dùng, Android 8.0 (API cấp 26) áp dụng các giới hạn về những việc ứng dụng có thể làm trong khi chạy ở chế độ nền. Tài liệu này mô tả các thay đổi đối với hệ điều hành và cách bạn có thể cập nhật ứng dụng để ứng dụng hoạt động tốt trong các giới hạn mới.

Tổng quan

Nhiều ứng dụng và dịch vụ Android có thể chạy đồng thời. Ví dụ: người dùng có thể chơi trò chơi trong một cửa sổ trong khi duyệt web trong một cửa sổ khác và sử dụng ứng dụng thứ ba để phát nhạc. Càng nhiều ứng dụng chạy cùng lúc, hệ thống càng phải chịu nhiều tải. Nếu các ứng dụng hoặc dịch vụ bổ sung đang chạy ở chế độ nền, thì việc này sẽ tạo thêm các tải trên hệ thống, điều này có thể mang lại trải nghiệm không tốt cho người dùng; ví dụ: ứng dụng âm nhạc có thể bị tắt đột ngột.

Để giảm nguy cơ xảy ra những sự cố này, Android 8.0 đặt ra các giới hạn về những việc ứng dụng có thể làm trong khi người dùng không tương tác trực tiếp với các ứng dụng đó. Ứng dụng bị hạn chế theo hai cách:

  • Giới hạn đối với dịch vụ nền: Mặc dù ứng dụng ở trạng thái rảnh, có những giới hạn đối với việc sử dụng dịch vụ nền. Điều này không áp dụng cho các dịch vụ trên nền trước mà người dùng dễ nhận thấy hơn.

  • Giới hạn truyền tin: Với một số ít trường hợp ngoại lệ, ứng dụng không thể sử dụng tệp kê khai để đăng ký các thông báo truyền phát ngầm. Chúng vẫn có thể đăng ký các thông báo truyền tin này trong thời gian chạy, đồng thời có thể sử dụng tệp kê khai để đăng ký các thông báo và thông báo rõ ràng được nhắm mục tiêu cụ thể tại ứng dụng của họ.

Trong hầu hết trường hợp, ứng dụng có thể khắc phục những hạn chế này bằng cách sử dụng tác vụ JobScheduler. Phương pháp này cho phép một ứng dụng sắp xếp thực hiện công việc khi ứng dụng không hoạt động, nhưng vẫn giúp hệ thống có thời gian để lên lịch các công việc này theo cách không ảnh hưởng đến trải nghiệm người dùng. Android 8.0 mang đến một số điểm cải tiến cho JobScheduler để giúp bạn dễ dàng thay thế các dịch vụ và broadcast receiver bằng các công việc theo lịch biểu; để biết thêm thông tin, hãy xem các điểm cải tiến của JobScheduler.

Các hạn chế đối với dịch vụ nền

Các dịch vụ chạy ở chế độ nền có thể tiêu tốn tài nguyên thiết bị, từ đó có khả năng dẫn đến trải nghiệm người dùng kém. Để giảm thiểu vấn đề này, hệ thống sẽ áp dụng một số giới hạn cho các dịch vụ.

Hệ thống phân biệt giữa ứng dụng nền trước và ứng dụng nền. (Định nghĩa về nền cho các mục đích của giới hạn dịch vụ khác với định nghĩa của tính năng quản lý bộ nhớ; ứng dụng có thể chạy ở chế độ nền liên quan đến việc quản lý bộ nhớ, nhưng ở nền trước liên quan đến khả năng chạy dịch vụ của ứng dụng.) Một ứng dụng được coi là chạy ở nền trước nếu bất kỳ trường hợp nào sau đây xảy ra:

  • Ứng dụng này có một hoạt động hiển thị, cho dù hoạt động đó là bắt đầu hay bị tạm dừng.
  • Ứng dụng này có một dịch vụ trên nền trước.
  • Một ứng dụng trên nền trước khác được kết nối với ứng dụng này bằng cách liên kết với một trong các dịch vụ của ứng dụng đó hoặc bằng cách sử dụng một trong các nhà cung cấp nội dung của ứng dụng đó. Ví dụ: ứng dụng chạy ở nền trước nếu một ứng dụng khác liên kết với ứng dụng đó:
    • IME
    • Dịch vụ hình nền
    • Trình xử lý thông báo
    • Dịch vụ thoại hoặc tin nhắn

Nếu không có điều kiện nào trong số đó đúng, ứng dụng được coi là đang chạy ở chế độ nền.

Khi chạy ở nền trước, ứng dụng có thể tạo và chạy cả dịch vụ trên nền trước và nền sau một cách tự do. Khi chuyển sang chạy ở chế độ nền, ứng dụng đó sẽ có một khoảng thời gian vài phút để tạo và sử dụng dịch vụ. Khi kết thúc cửa sổ đó, ứng dụng sẽ được coi là không hoạt động. Tại thời điểm này, hệ thống sẽ dừng các dịch vụ nền của ứng dụng, giống như khi ứng dụng đã gọi các phương thức Service.stopSelf() của dịch vụ đó.

Trong một số trường hợp, ứng dụng nền sẽ được đưa vào danh sách cho phép tạm thời trong vài phút. Khi nằm trong danh sách cho phép, một ứng dụng có thể chạy các dịch vụ mà không giới hạn và các dịch vụ nền của ứng dụng đó được phép chạy. Một ứng dụng sẽ được đưa vào danh sách cho phép khi xử lý một tác vụ mà người dùng có thể nhìn thấy, chẳng hạn như:

Trong nhiều trường hợp, ứng dụng của bạn có thể thay thế các dịch vụ nền bằng các công việc JobScheduler. Chẳng hạn, CoolPhotosApp cần kiểm tra xem người dùng có nhận được ảnh do bạn bè chia sẻ hay không, ngay cả khi ứng dụng không chạy ở nền trước. Trước đây, ứng dụng sử dụng dịch vụ nền đã kiểm tra với bộ nhớ trên đám mây của ứng dụng. Để di chuyển sang Android 8.0 (API cấp 26), nhà phát triển sẽ thay thế dịch vụ nền bằng một công việc theo lịch. Công việc này được khởi chạy định kỳ, truy vấn máy chủ rồi thoát.

Trước Android 8.0, cách thông thường để tạo dịch vụ trên nền trước là tạo dịch vụ nền, sau đó quảng bá dịch vụ đó lên nền trước. Với Android 8.0, có một chức năng; hệ thống không cho phép ứng dụng nền tạo dịch vụ nền. Vì lý do này, Android 8.0 giới thiệu phương thức startForegroundService() mới để bắt đầu một dịch vụ mới trên nền trước. Sau khi hệ thống tạo dịch vụ, ứng dụng sẽ có 5 giây để gọi phương thức [startForeground()](/reference/android/app/Service#startForeground(int, android.app.Notification) của dịch vụ để hiển thị thông báo mà người dùng thấy của dịch vụ mới. Nếu ứng dụng không gọi startForeground() trong thời gian giới hạn, hệ thống sẽ dừng dịch vụ và khai báo ứng dụng đó là ANR.

Giới hạn truyền phát

Nếu một ứng dụng đăng ký nhận thông báo truyền tin, thì trình thu nhận của ứng dụng sẽ sử dụng tài nguyên mỗi khi thông báo truyền tin được gửi đi. Điều này có thể gây ra sự cố nếu có quá nhiều ứng dụng đăng ký nhận thông báo dựa trên các sự kiện hệ thống; một sự kiện hệ thống kích hoạt một thông báo truyền tin có thể khiến tất cả các ứng dụng đó tiêu thụ tài nguyên liên tiếp và nhanh chóng, ảnh hưởng đến trải nghiệm người dùng. Để giảm thiểu vấn đề này, Android 7.0 (API cấp 24) đã đặt các giới hạn đối với các thông báo truyền tin, như mô tả trong phần Tối ưu hoá ở chế độ nền. Android 8.0 (API cấp 26) khiến những hạn chế này trở nên nghiêm ngặt hơn.

  • Các ứng dụng nhắm mục tiêu đến Android 8.0 trở lên không thể đăng ký broadcast receiver cho các thông báo truyền phát ngầm trong tệp kê khai, trừ phi thông báo truyền tin bị hạn chế riêng cho ứng dụng đó. Thông báo truyền phát ngầm là một thông báo không nhắm đến một thành phần cụ thể trong ứng dụng. Ví dụ: ACTION_PACKAGE_REPLACED được gửi đến tất cả trình nghe đã đăng ký trên mọi ứng dụng để cho họ biết rằng một số gói trên thiết bị đã được thay thế. Vì thông báo truyền tin được ngầm định nên sẽ không được phân phối tới bộ thu đã đăng ký tệp kê khai trong các ứng dụng nhắm mục tiêu đến Android 8.0 trở lên. ACTION_MY_PACKAGE_REPLACED cũng là thông báo truyền phát ngầm, nhưng vì thông báo này chỉ được gửi đến ứng dụng có gói đã được thay thế, nên sẽ được phân phối đến trình thu nhận đã đăng ký tệp kê khai.
  • Ứng dụng có thể tiếp tục đăng ký các thông báo phản cảm trong tệp kê khai.
  • Các ứng dụng có thể dùng Context.registerReceiver() trong thời gian chạy để đăng ký receiver cho mọi thông báo truyền tin, cho dù là ngầm ẩn hay tường minh.
  • Các thông báo truyền tin yêu cầu quyền chữ ký sẽ được miễn trừ khỏi hạn chế này vì những thông báo truyền tin này chỉ được gửi đến các ứng dụng được ký bằng cùng một chứng chỉ, chứ không phải tới tất cả các ứng dụng trên thiết bị.

Trong nhiều trường hợp, những ứng dụng từng đăng ký thông báo truyền phát ngầm có thể nhận được chức năng tương tự bằng cách dùng công việc JobScheduler. Ví dụ: Thỉnh thoảng, một ứng dụng ảnh trên mạng xã hội có thể cần dọn dẹp dữ liệu của mình và muốn thực hiện việc này khi thiết bị được kết nối với bộ sạc. Trước đó, ứng dụng đã đăng ký trình thu nhận cho ACTION_POWER_CONNECTED trong tệp kê khai; khi nhận được thông báo đó, ứng dụng sẽ kiểm tra xem có cần dọn dẹp hay không. Để di chuyển sang Android 8.0 trở lên, ứng dụng sẽ xoá bộ nhận đó khỏi tệp kê khai. Thay vào đó, ứng dụng sẽ lên lịch cho một công việc dọn dẹp chạy khi thiết bị ở trạng thái rảnh và đang sạc.

Hướng dẫn di chuyển

Theo mặc định, các thay đổi này chỉ ảnh hưởng đến những ứng dụng nhắm đến Android 8.0 (API cấp 26) trở lên. Tuy nhiên, người dùng có thể bật các hạn chế này cho bất kỳ ứng dụng nào trên màn hình Cài đặt, ngay cả khi ứng dụng đó nhắm đến một cấp độ API thấp hơn 26. Bạn có thể cần cập nhật ứng dụng để đáp ứng các giới hạn mới.

Kiểm tra để xem cách ứng dụng của bạn sử dụng dịch vụ. Nếu ứng dụng của bạn dựa vào các dịch vụ chạy ở chế độ nền khi ứng dụng ở trạng thái rảnh, thì bạn cần phải thay thế các dịch vụ đó. Các giải pháp khả thi bao gồm:

  • Nếu ứng dụng của bạn cần tạo một dịch vụ trên nền trước trong khi ứng dụng đang chạy ở chế độ nền, hãy dùng phương thức startForegroundService() thay vì startService().
  • Nếu người dùng có thể nhận thấy dịch vụ này, hãy đặt dịch vụ đó làm dịch vụ trên nền trước. Ví dụ: dịch vụ phát âm thanh phải luôn là dịch vụ trên nền trước. Tạo dịch vụ bằng phương thức startForegroundService() thay vì startService().
  • Tìm cách sao chép chức năng của dịch vụ bằng một công việc đã lên lịch. Nếu dịch vụ không thực hiện thao tác nào đó ngay lập tức mà người dùng nhận thấy, thì thường thì bạn có thể sử dụng một công việc đã lên lịch.
  • Sử dụng FCM để đánh thức ứng dụng một cách có chọn lọc khi các sự kiện mạng xảy ra, thay vì thăm dò ý kiến trong nền.
  • Hoãn công việc trong nền cho đến khi ứng dụng chạy tự nhiên ở nền trước.

Xem lại broadcast receiver được xác định trong tệp kê khai của ứng dụng. Nếu tệp kê khai của bạn khai báo trình thu nhận cho một thông báo truyền phát ngầm bị ảnh hưởng, bạn phải thay thế trình thu nhận thông báo đó. Các giải pháp khả thi bao gồm:

  • Hãy tạo receiver trong thời gian chạy bằng cách gọi Context.registerReceiver(), thay vì khai báo receiver trong tệp kê khai.
  • Sử dụng công việc theo lịch để kiểm tra điều kiện đã kích hoạt thông báo truyền phát ngầm.