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

Bất cứ khi nào chạy ở chế độ nền, ứ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ể làm giảm trải nghiệm người dùng, đặc biệt là nếu 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 đặt 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 để 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ể đang 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 một 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 trong nền, thì điều này sẽ gây thêm tải cho hệ thống, có thể dẫn đến trải nghiệm người dùng kém; ví dụ: ứng dụng nhạc có thể đột ngột tắt.

Để giảm khả năng xảy ra những vấn đề 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 khi người dùng không tương tác trực tiếp với ứ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: Khi ứng dụng ở trạng thái rảnh, ứng dụng đó sẽ có giới hạn 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, vì người dùng dễ nhận thấy các dịch vụ này hơn.

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

Trong hầu hết các 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 công việc JobScheduler. Phương pháp này cho phép ứng dụng sắp xếp để thực hiện công việc khi ứng dụng không hoạt động tích cực, nhưng vẫn cho phép hệ thống có cơ hội 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 cung cấp 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ông việc theo lịch; để biết thêm thông tin, hãy xem phần Cải tiến JobScheduler.

Giới hạn của dịch vụ trong 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ó thể dẫn đến trải nghiệm người dùng kém hơn. Để giảm thiểu vấn đề này, hệ thống áp dụng một số giới hạn đối với các dịch vụ.

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

  • Có một hoạt động hiển thị, cho dù hoạt động đó đã bắt đầu hay 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 ở nền trước nếu một ứng dụng khác liên kết với:
    • 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, thì ứng dụng được coi là đang chạy ở chế độ nền.

Khi ở chế độ nền trước, ứng dụng có thể tự do tạo và chạy cả dịch vụ trên nền trước và dịch vụ trên nền sau. Khi chuyển sang chế độ nền, ứng dụng sẽ có một khoảng thời gian vài phút để vẫn có thể tạo và sử dụng các dịch vụ. Khi kết thúc khoảng thời gian đó, ứng dụng được coi là rảnh. Tại thời điểm này, hệ thống sẽ dừng các dịch vụ trong 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 chạy 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, ứng dụng có thể chạy các dịch vụ mà không bị hạn chế và các dịch vụ trong nền của ứng dụng được phép chạy. Ứng dụng được đưa vào danh sách cho phép khi ứng dụng xử lý một tác vụ mà người dùng có thể 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ụ trong nền bằng các công việc JobScheduler. Ví dụ: CoolPhotoApp cần kiểm tra xem người dùng có nhận được ảnh được chia sẻ từ bạn bè 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 một 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 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 một dịch vụ trong nền, sau đó chuyển dịch vụ đó lên nền trước. Với Android 8.0, có một vấn đề; hệ thống không cho phép ứng dụng chạy nền tạo dịch vụ chạy nền. Vì lý do này, Android 8.0 giới thiệu phương thức mới startForegroundService() để bắt đầu một dịch vụ mới ở nền trước. Sau khi hệ thống tạo dịch vụ, ứng dụng 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 có thể nhìn thấy của dịch vụ mới. Nếu ứng dụng không gọi startForeground() trong giới hạn thời gian, hệ thống sẽ dừng dịch vụ và khai báo ứng dụng là ANR.

Giới hạn đối với tính năng phát sóng

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

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

Trong nhiều trường hợp, các ứng dụng đã đăng ký trước để phát sóng ngầm có thể nhận được chức năng tương tự bằng cách sử dụng công việc JobScheduler. Ví dụ: ứng dụng ảnh xã hội có thể cần phải dọn dẹp dữ liệu của mình theo thời gian và nê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 đây, ứng dụng đã đăng ký một bộ thu cho ACTION_POWER_CONNECTED trong tệp kê khai; khi nhận được thông báo truyền tin đó, ứ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á trình nhận đó khỏi tệp kê khai. Thay vào đó, ứng dụng sẽ lên lịch 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, những thay đổi này chỉ ảnh hưởng đến các ứ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 cấp độ API thấp hơn 26. Bạn có thể cần cập nhật ứng dụng để tuân thủ các giới hạn mới.

Kiểm tra cách ứng dụng của bạn sử dụng các 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 trong khi ứng dụng không hoạt động, bạn sẽ cần thay thế các dịch vụ đó. Sau đây là một số giải pháp có thể áp dụng:

  • 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 sử 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ụ, hãy đặt dịch vụ đó thành 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 được lên lịch. Nếu dịch vụ không làm gì đó mà người dùng có thể nhận thấy ngay lập tức, thì bạn thường có thể sử dụng công việc đượ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 ở chế độ nền.
  • Tạm hoãn công việc trong nền cho đến khi ứng dụng tự nhiên chạy ở nền trước.

Xem xét các 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 một trình nhận cho một thông báo truyền tin ngầm bị ảnh hưởng, bạn phải thay thế trình nhận đó. Sau đây là một số giải pháp có thể áp dụng:

  • Tạo trình nhận trong thời gian chạy bằng cách gọi Context.registerReceiver(), thay vì khai báo trình nhận 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 tin ngầm.