Bộ nhớ đệm ứng dụng

Android 11 (cấp độ API 30) trở lên hỗ trợ tính năng đóng băng ứng dụng được lưu vào bộ nhớ đệm. Tính năng này sẽ dừng thực thi cho các quy trình được lưu vào bộ nhớ đệm và giảm mức sử dụng tài nguyên của các ứng dụng hoạt động không đúng cách có thể cố gắng hoạt động trong khi được lưu vào bộ nhớ đệm.

Tính năng đóng băng ứng dụng được lưu vào bộ nhớ đệm sẽ giữ các ứng dụng trong RAM trong khi vẫn tắt chúng khỏi CPU. Nếu Android xác định rằng một ứng dụng không nên thực hiện công việc nhưng có thể cần thiết trong tương lai, thì hệ thống sẽ đóng băng quy trình của ứng dụng thay vì chấm dứt quy trình đó. Điều này ngăn chặn quá trình khởi động nguội khi ứng dụng cần thiết lại.

Android đóng băng các ứng dụng được lưu vào bộ nhớ đệm bằng cách di chuyển các quy trình của ứng dụng vào một cgroup đóng băng. Điều này giúp giảm mức tiêu thụ CPU khi đang hoạt động và khi không hoạt động trong trường hợp có các ứng dụng đang hoạt động được lưu vào bộ nhớ đệm. Bạn có thể bật tính năng đóng băng ứng dụng bằng cờ cấu hình hệ thống hoặc lựa chọn dành cho nhà phát triển.

Trong Android 14 (cấp độ API 34) trở lên, tính năng đóng băng ứng dụng được lưu vào bộ nhớ đệm có các hành vi mạnh mẽ sau đây:

  • Các quy trình ứng dụng ở trạng thái đã lưu vào bộ nhớ đệm sẽ bị đóng băng 10 giây sau khi chuyển sang trạng thái đã lưu vào bộ nhớ đệm.
  • Hệ thống sẽ ngay lập tức huỷ đóng băng một quy trình ứng dụng bị đóng băng trong một sự kiện vòng đời. Các sự kiện này bao gồm việc nhận một ý định, bắt đầu một dịch vụ công việc hoặc người dùng tiếp tục một hoạt động.

ActivityManagerService quản lý tất cả các quy trình của ứng dụng và đưa ra quyết định về vòng đời của ứng dụng. CachedAppOptimizer chịu trách nhiệm làm treo quy trình ứng dụng.

Khi một quy trình ứng dụng bị đóng băng, tất cả các luồng của quy trình đó sẽ bị tạm ngưng và không thể thực hiện công việc trên CPU cho đến khi được giải phóng. Do đó, ứng dụng không thể thực hiện thu gom rác (GC) và không thể phản hồi các sự kiện giảm bộ nhớ. Để biết thông tin chi tiết, hãy xem ComponentCallbacks2.onTrimMemory(int). Để đáp ứng yêu cầu này, kể từ Android 14:

  • Những ứng dụng có một thực thể Activity hiển thị sẽ được thông báo về TRIM_MEMORY_UI_HIDDEN ngay khi chuyển sang nền. Những ứng dụng duy trì vòng đời mà không có giao diện người dùng, chẳng hạn như ứng dụng có dịch vụ trên nền trước, có thể nhận được TRIM_MEMORY_BACKGROUND. Các sự kiện cắt khác sẽ không được gửi, vì khi đủ điều kiện nhận những sự kiện đó, các ứng dụng dự kiến sẽ bị đóng băng.
  • Ngay sau khi chuyển sang trạng thái đã lưu vào bộ nhớ đệm, hệ thống có thể yêu cầu thời gian chạy ứng dụng thực hiện một GC để chuẩn bị cho việc có thể bị đóng băng.
  • Khi một quy trình ứng dụng bị đóng băng, các bước nén bộ nhớ bổ sung có thể xảy ra, chẳng hạn như ghi các trang có sửa đổi vào bộ nhớ dự phòng và hoán đổi các trang ẩn danh sang ZRAM.
  • Nếu tất cả các quy trình cho một ứng dụng cụ thể bị đóng băng, hệ thống sẽ chấm dứt mọi ổ cắm TCP đang hoạt động do ứng dụng duy trì. Điều này ngăn phía máy chủ của ổ cắm gửi các lệnh ping TCP keepalive (giữ kết nối) có thể đánh thức modem của thiết bị.

Các quy trình ứng dụng được lưu vào bộ nhớ đệm sẽ được giải phóng khi trạng thái quy trình của chúng tăng từ trạng thái được lưu vào bộ nhớ đệm lên trạng thái có tầm quan trọng cao hơn. Để giảm các sự kiện giải phóng trong Android 14 trở lên, hệ thống sẽ đưa các tin truyền đã đăng ký theo bối cảnh vào hàng đợi trong khi ứng dụng ở trạng thái được lưu vào bộ nhớ đệm. Tin truyền đã đăng ký theo bối cảnh là những receiver mà một ứng dụng đăng ký một cách linh động bằng cách gọi Context.registerReceiver. Hệ thống chỉ phân phối những thông báo truyền tin đã xếp hàng này sau khi ứng dụng được rã đông. Ngược lại, hệ thống không xếp hàng các thông báo truyền tin được khai báo trong tệp kê khai. Các tin truyền do tệp kê khai khai báo là các dịch vụ nhận được khai báo tĩnh trong AndroidManifest.xml bằng phần tử <receiver>. Hệ thống sẽ ngay lập tức giải phóng ứng dụng đã lưu vào bộ nhớ đệm để phân phối các tin truyền do tệp kê khai khai báo.

Tác động đến tình trạng hệ thống

Android sẽ chấm dứt quy trình ứng dụng được lưu vào bộ nhớ đệm được dùng gần đây nhất nếu có nhiều hơn MAX_CACHED_PROCESSES quy trình ứng dụng được lưu vào bộ nhớ đệm. Trên các thiết bị được hỗ trợ chạy Android 14 trở lên, MAX_CACHED_PROCESSES sẽ tăng lên đáng kể, cho phép các thiết bị duy trì nhiều quy trình ứng dụng được lưu vào bộ nhớ đệm hơn đáng kể trong RAM.

Việc duy trì nhiều ứng dụng được lưu vào bộ nhớ đệm trong RAM giúp giảm đến 30% số lượt khởi động nguội, với mức giảm tỷ lệ thuận với tổng dung lượng RAM của thiết bị. Đồng thời, mức tiêu thụ CPU của các ứng dụng được lưu vào bộ nhớ đệm sẽ giảm thiểu, giúp tiết kiệm pin đáng kể.

Các trường hợp miễn trừ đối với tủ đông

Trong một số điều kiện nhất định, quy trình ứng dụng có thể chuyển sang trạng thái đã lưu vào bộ nhớ đệm nhưng vẫn không bị đóng băng. Những trường hợp ngoại lệ này là thông tin chi tiết về việc triển khai và có thể thay đổi trong các phiên bản Android sau này:

  • Khoá tệp: Nếu một quy trình được lưu vào bộ nhớ đệm giữ một khoá tệp chặn các quy trình khác không được lưu vào bộ nhớ đệm, thì quy trình giữ khoá sẽ không bị đóng băng.
  • Các liên kết BIND_WAIVE_PRIORITY: Các quy trình ứng dụng có các liên kết đến được tạo bằng Context.BIND_WAIVE_PRIORITY có thể chuyển sang trạng thái được lưu vào bộ nhớ đệm nhưng vẫn không bị đóng băng cho đến khi tất cả các quy trình ứng dụng khách được kết nối cũng được lưu vào bộ nhớ đệm. Ngoại lệ này hỗ trợ các ứng dụng có nhiều quy trình, chẳng hạn như trình duyệt web sử dụng Thẻ tuỳ chỉnh.

Triển khai tính năng đóng băng ứng dụng

Trình đóng băng ứng dụng được lưu vào bộ nhớ đệm tận dụng trình đóng băng cgroup v2 của nhân. Các thiết bị đi kèm với một nhân tương thích có thể bật tính năng này. Bật tuỳ chọn cho nhà phát triển Suspend execution for cached apps (Tạm ngưng thực thi cho các ứng dụng được lưu vào bộ nhớ đệm) hoặc đặt cờ cấu hình thiết bị activity_manager_native_boot use_freezer thành true. Ví dụ:

adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot

Tính năng đóng băng sẽ bị vô hiệu hoá khi bạn đặt cờ use_freezer thành false hoặc vô hiệu hoá lựa chọn dành cho nhà phát triển. Ví dụ:

adb shell device_config put activity_manager_native_boot use_freezer false && adb reboot

Bạn có thể bật/tắt chế độ cài đặt này bằng cách thay đổi cấu hình thiết bị trong một bản phát hành hoặc cập nhật phần mềm.

Để ghi đè MAX_CACHED_PROCESSES, ví dụ: để đặt giá trị thành 1024 cho mục đích kiểm thử:

adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistent

Cách huỷ chế độ ghi đè MAX_CACHED_PROCESSES:

adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests none

Trình đóng băng ứng dụng không hiển thị các API chính thức và không có ứng dụng tham chiếu, nhưng trình này sử dụng các API hệ thống ẩn setProcessFrozen để đóng băng một quy trình riêng lẻ và enableFreezer để bật hoặc tắt tính năng đóng băng trên toàn cầu.

Xử lý các tính năng tuỳ chỉnh

Các quy trình của ứng dụng không được phép thực hiện bất kỳ thao tác nào khi được lưu vào bộ nhớ đệm, nhưng một số ứng dụng có thể có các tính năng tuỳ chỉnh được hỗ trợ bởi các quy trình dự kiến sẽ chạy trong khi được lưu vào bộ nhớ đệm. Khi trình đóng băng ứng dụng được bật trên một thiết bị đang chạy các ứng dụng như vậy, các quy trình được lưu vào bộ nhớ đệm sẽ bị đóng băng và có thể ngăn các tính năng tuỳ chỉnh hoạt động.

Để khắc phục, bạn có thể thay đổi trạng thái của quy trình thành noncached trước khi quy trình cần thực hiện bất kỳ công việc nào. Thay đổi này giúp các ứng dụng luôn hoạt động. Ví dụ về trạng thái hoạt động bao gồm dịch vụ trên nền trước được liên kết hoặc trạng thái nền trước.

Các chế độ lỗi thường gặp

Khi các quy trình của ứng dụng bị treo, hoạt động giao tiếp liên quy trình (IPC) hoặc lập lịch tác vụ không đúng cách có thể dẫn đến việc ứng dụng bị chấm dứt hoặc có hành vi không mong muốn.

Giao dịch nhị phân đồng bộ với các quy trình bị tạm dừng

Khi một quy trình ứng dụng khách gửi một giao dịch liên kết đồng bộ đến một quy trình ứng dụng máy chủ bị đóng băng, hệ thống sẽ ngay lập tức chấm dứt quy trình ứng dụng máy chủ. Điều này ngăn luồng ứng dụng chặn vô thời hạn trong khi chờ phản hồi từ máy chủ bị treo. Sau đó, luồng ứng dụng sẽ nhận được RemoteException và mọi trình nghe đã đăng ký sẽ được kích hoạt. Để biết chi tiết, hãy xem IBinder.linkToDeath.

Nguyên nhân gốc: Lỗi này thường do một lỗi trong ứng dụng khách gây ra. Khi một ứng dụng khách liên kết với một dịch vụ, quy trình máy chủ sẽ được liên kết với ứng dụng khách và không thể chuyển sang trạng thái được lưu vào bộ nhớ đệm trước khi ứng dụng khách thực hiện. Để biết thông tin chi tiết, hãy xem Context.bindService. Tuy nhiên, sau khi ứng dụng gọi Context.unbindService, quy trình máy chủ có thể được lưu vào bộ nhớ đệm và bị đóng băng. Nếu tiếp tục sử dụng tham chiếu IBinder được lưu vào bộ nhớ đệm sau khi huỷ liên kết, ứng dụng có nguy cơ giao tiếp với một quy trình bị treo.

Để tránh vấn đề này, hãy đảm bảo các ứng dụng khách loại bỏ các tham chiếu IBinder ngay sau khi gọi Context.unbindService.

Tràn bộ đệm giao dịch liên kết không đồng bộ

Khi một quy trình ứng dụng máy chủ nhận được các giao dịch nhị phân không đồng bộ (oneway) trong khi bị đóng băng, các giao dịch sẽ được lưu vào bộ nhớ đệm theo từng quy trình. Nếu máy chủ nhận được quá nhiều giao dịch không đồng bộ trong khi bị đóng băng, bộ nhớ đệm sẽ tràn và hệ thống sẽ chấm dứt quy trình ứng dụng máy chủ.

Để ngăn chặn tình trạng tràn bộ nhớ đệm này, hãy tránh gửi quá nhiều giao dịch liên kết không đồng bộ đến các quy trình có thể được lưu vào bộ nhớ đệm hoặc bị đóng băng.

Thực hiện lại các việc cần làm theo lịch khi hệ thống hoạt động trở lại

Nếu một ứng dụng thực hiện các tác vụ lặp lại, thì các tác vụ đó sẽ bị tạm dừng trong khi quy trình bị đóng băng. Để biết thông tin chi tiết, hãy xem ScheduledThreadPoolExecutor.scheduleAtFixedRate hoặc Timer.scheduleAtFixedRate. Khi quá trình này ngừng bị treo, các lần thực thi bị bỏ lỡ tích luỹ có thể chạy liên tục với tốc độ nhanh mà hầu như không có độ trễ.

Để ngăn chặn tình trạng tăng đột biến số lần thực thi khi ứng dụng ngừng đóng băng, hãy dùng scheduleWithFixedDelay thay vì scheduleAtFixedRate cho các tác vụ ở chế độ nền. Bạn cũng có thể dùng phím tắt WorkManager.

Kiểm thử và khắc phục sự cố liên quan đến tính năng đóng băng ứng dụng

Để xác minh rằng app freezer đang hoạt động như dự kiến hoặc để khắc phục các vấn đề liên quan đến freezer, hãy sử dụng các công cụ và lệnh chẩn đoán sau:

Các lệnh của trình quản lý hoạt động

Bạn có thể dùng các lệnh adb shell am để kiểm soát thủ công việc đóng băng và nén cho một quy trình cụ thể:

  • Buộc một quy trình bị treo:

    adb shell am freeze <process>
  • Buộc một quy trình hủy cố định:

    adb shell am unfreeze <process>
  • Buộc nén toàn bộ bộ nhớ trên một quy trình:

    adb shell am compact full <process>

Kiểm tra Logcat

Xem logcat để xem các mục nhập bị đóng băng và không bị đóng băng mỗi khi một quy trình di chuyển vào hoặc ra khỏi trình đóng băng:

adb logcat | grep -i "\(freezing\|froze\)"

Các giá trị được liệt kê đầu ra của nhật ký lý do huỷ đóng băng từ enum bộ đệm giao thức UnfreezeReason.

Kiểm tra dumpsys

Kiểm tra danh sách các quy trình bị treo bằng cách sử dụng dumpsys activity:

adb shell dumpsys activity | grep -A 20 "Apps frozen:"

Kiểm tra xem có tệp /sys/fs/cgroup/uid_0/cgroup.freeze hay không.

ApplicationExitInfo

Để truy vấn lý do chấm dứt quy trình trước đó, hãy xem ActivityManager.getHistoricalProcessExitReasons. Nếu một quy trình ứng dụng bị chấm dứt do vấn đề liên quan đến trình đóng băng, chẳng hạn như nhận giao dịch liên kết đồng bộ trong khi bị đóng băng, thì lý do thoát sẽ được đặt thành ApplicationExitInfo.REASON_FREEZER.

Theo dõi Perfetto

Các sự kiện liên quan đến cơ chế đóng băng được phát ra một dấu vết có tên là Freezer trong quy trình system_server trong dấu vết Perfetto:

  • Các lát FreezeUnfreeze cho biết thời điểm một quy trình thay đổi trạng thái.
  • Sự kiện updateAppFreezeStateLSP cho biết thời điểm máy chủ hệ thống kiểm tra lại các thuộc tính quy trình để đưa ra quyết định đóng băng hoặc giải phóng.

Bạn có thể kiểm tra các sự kiện này ngay trong giao diện người dùng Perfetto hoặc phân tích chúng bằng PerfettoSQL:

INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");

Trong thư viện chuẩn PerfettoSQL, các sự kiện đóng băng cũng được tóm tắt trong bảng android_freezer_events.