Khi sử dụng binder để giao tiếp giữa các quy trình, hãy đặc biệt chú ý khi quy trình từ xa ở trạng thái được lưu vào bộ nhớ đệm hoặc bị đóng băng. Các lệnh gọi vào ứng dụng được lưu vào bộ nhớ đệm hoặc bị đóng băng có thể khiến ứng dụng gặp sự cố hoặc tiêu thụ tài nguyên một cách không cần thiết.
Trạng thái ứng dụng được lưu vào bộ nhớ đệm và bị đóng băng
Android duy trì các ứng dụng ở nhiều trạng thái để quản lý tài nguyên hệ thống như bộ nhớ và CPU.
Trạng thái được lưu vào bộ nhớ đệm
Khi một ứng dụng không có thành phần nào mà người dùng nhìn thấy, chẳng hạn như hoạt động hoặc dịch vụ, ứng dụng đó có thể được chuyển sang trạng thái được lưu vào bộ nhớ đệm. Hãy xem phần Các quy trình và vòng đời của ứng dụng để biết thông tin chi tiết. Các ứng dụng được lưu vào bộ nhớ đệm sẽ được giữ trong bộ nhớ trong trường hợp người dùng chuyển về các ứng dụng đó, nhưng các ứng dụng này không được mong đợi là đang hoạt động.
Khi liên kết từ một quy trình ứng dụng này sang một quy trình ứng dụng khác, chẳng hạn như sử dụng bindService, trạng thái quy trình của quy trình máy chủ sẽ được nâng lên ít nhất là quan trọng như quy trình ứng dụng (trừ phi bạn chỉ định Context#BIND_WAIVE_PRIORITY). Ví dụ: nếu máy khách không ở trạng thái được lưu vào bộ nhớ đệm, thì máy chủ cũng vậy.
Ngược lại, trạng thái của một quy trình máy chủ không xác định trạng thái của các ứng dụng. Vì vậy, một máy chủ có thể có các kết nối liên kết với ứng dụng, thường ở dạng lệnh gọi lại và trong khi quy trình từ xa ở trạng thái được lưu vào bộ nhớ đệm, máy chủ sẽ không được lưu vào bộ nhớ đệm.
Khi thiết kế các API mà lệnh gọi lại bắt nguồn từ một quy trình nâng cao và được gửi đến các ứng dụng, hãy cân nhắc việc tạm dừng gửi lệnh gọi lại khi một ứng dụng chuyển sang trạng thái đã lưu vào bộ nhớ đệm và tiếp tục khi ứng dụng thoát khỏi trạng thái này. Điều này giúp ngăn chặn các thao tác không cần thiết trong các quy trình ứng dụng được lưu vào bộ nhớ đệm.
Để theo dõi thời điểm các ứng dụng chuyển sang hoặc thoát khỏi trạng thái được lưu vào bộ nhớ đệm, hãy dùng ActivityManager.addOnUidImportanceListener:
// in ActivityManager or Context
activityManager.addOnUidImportanceListener(
new UidImportanceListener() { ... },
IMPORTANCE_CACHED);
Trạng thái đóng băng
Hệ thống có thể đóng băng một ứng dụng được lưu vào bộ nhớ đệm để tiết kiệm tài nguyên. Khi một ứng dụng bị đóng băng, ứng dụng đó sẽ không nhận được thời gian CPU và không thể thực hiện bất kỳ thao tác nào. Để biết thêm thông tin chi tiết, hãy xem phần Trình đóng băng ứng dụng được lưu vào bộ nhớ đệm.
Khi một quy trình gửi giao dịch liên kết đồng bộ (không phải oneway) đến một quy trình từ xa khác đang bị treo, hệ thống sẽ tắt quy trình từ xa đó. Điều này ngăn luồng gọi trong quy trình gọi bị treo vô thời hạn trong khi chờ quy trình từ xa được rã đông, điều này có thể gây ra tình trạng thiếu luồng hoặc bế tắc trong ứng dụng gọi.
Khi một quy trình gửi giao dịch liên kết không đồng bộ (oneway) đến một ứng dụng bị đóng băng (thường bằng cách thông báo cho một lệnh gọi lại, thường là phương thức oneway), giao dịch sẽ được lưu vào bộ nhớ đệm cho đến khi quy trình từ xa được mở băng. Nếu bộ đệm tràn, quy trình ứng dụng nhận có thể gặp sự cố. Ngoài ra, các giao dịch được lưu vào bộ nhớ đệm có thể trở nên cũ vào thời điểm quy trình ứng dụng được rã đông và xử lý các giao dịch đó.
Để tránh làm quá tải các ứng dụng bằng các sự kiện cũ hoặc làm tràn bộ đệm của các ứng dụng đó, bạn phải tạm dừng gửi lệnh gọi lại trong khi quy trình của ứng dụng nhận bị đóng băng.
Để theo dõi thời điểm ứng dụng bị đóng băng hoặc không bị đóng băng, hãy dùng IBinder.addFrozenStateChangeCallback:
// The binder token of the remote process
IBinder binder = service.getBinder();
// Keep track of frozen state
AtomicBoolean remoteFrozen = new AtomicBoolean(false);
// Update remoteFrozen when the remote process freezes or unfreezes
binder.addFrozenStateChangeCallback(
myExecutor,
new IBinder.FrozenStateChangeCallback() {
@Override
public void onFrozenStateChanged(boolean isFrozen) {
remoteFrozen.set(isFrozen);
}
});
// When dispatching callbacks to the remote process, pause dispatch if frozen:
if (!remoteFrozen.get()) {
// dispatch callback to remote process
}
Sử dụng RemoteCallbackList
Lớp RemoteCallbackList là một lớp hỗ trợ để quản lý danh sách các lệnh gọi lại IInterface mà các quy trình từ xa đăng ký. Lớp này tự động xử lý thông báo về sự cố liên kết và cung cấp các lựa chọn để xử lý lệnh gọi lại cho các ứng dụng bị treo.
Khi tạo RemoteCallbackList, bạn có thể chỉ định chính sách người gọi cố định:
FROZEN_CALLEE_POLICY_DROP: Các lệnh gọi lại đến ứng dụng bị đóng băng sẽ bị loại bỏ âm thầm. Sử dụng chính sách này khi các sự kiện xảy ra trong khi ứng dụng được lưu vào bộ nhớ đệm không quan trọng đối với ứng dụng, chẳng hạn như các sự kiện cảm biến theo thời gian thực.FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT: Nếu nhiều lệnh gọi lại được truyền tin khi một ứng dụng bị tạm ngưng, thì chỉ lệnh gọi lại gần đây nhất được đưa vào hàng đợi và gửi khi ứng dụng được huỷ tạm ngưng. Điều này hữu ích cho các lệnh gọi lại dựa trên trạng thái, trong đó chỉ có bản cập nhật trạng thái gần đây nhất là quan trọng, ví dụ: lệnh gọi lại thông báo cho ứng dụng về âm lượng hiện tại của nội dung nghe nhìn.FROZEN_CALLEE_POLICY_ENQUEUE_ALL: Tất cả lệnh gọi lại được truyền tin khi một ứng dụng bị đóng băng sẽ được đưa vào hàng đợi và gửi khi ứng dụng được mở băng. Hãy thận trọng với chính sách này, vì chính sách này có thể dẫn đến tình trạng tràn bộ đệm nếu có quá nhiều lệnh gọi lại được xếp hàng hoặc dẫn đến việc tích luỹ các sự kiện cũ.
Ví dụ sau đây cho thấy cách tạo và sử dụng một thực thể RemoteCallbackList loại bỏ các lệnh gọi lại cho các ứng dụng bị đóng băng:
RemoteCallbackList<IMyCallbackInterface> callbacks =
new RemoteCallbackList.Builder<IMyCallbackInterface>(
RemoteCallbackList.FROZEN_CALLEE_POLICY_DROP)
.setExecutor(myExecutor)
.build();
// Registering a callback:
callbacks.register(callback);
// Broadcasting to all registered callbacks:
callbacks.broadcast((callback) -> callback.onSomeEvent(eventData));
Nếu bạn sử dụng FROZEN_CALLEE_POLICY_DROP, hệ thống sẽ chỉ gọi callback.onSomeEvent() nếu quy trình lưu trữ lệnh gọi lại không bị đóng băng.
Dịch vụ hệ thống và hoạt động tương tác với ứng dụng
Các dịch vụ hệ thống thường tương tác với nhiều ứng dụng khác nhau bằng binder. Vì các ứng dụng có thể chuyển sang trạng thái được lưu vào bộ nhớ đệm và trạng thái bị đóng băng, nên các dịch vụ hệ thống phải đặc biệt chú ý để xử lý những hoạt động tương tác này một cách hiệu quả nhằm duy trì tính ổn định và hiệu suất của hệ thống.
Các dịch vụ hệ thống đã cần xử lý những trường hợp quy trình ứng dụng bị tắt vì nhiều lý do. Điều này liên quan đến việc dừng hoạt động thay cho họ và không cố gắng tiếp tục gửi lệnh gọi lại đến các quy trình không hoạt động. Việc cân nhắc đóng băng các ứng dụng là một phần mở rộng của trách nhiệm giám sát hiện có này.
Theo dõi trạng thái ứng dụng từ các dịch vụ hệ thống
Các dịch vụ hệ thống chạy trong system_server hoặc dưới dạng các trình nền gốc cũng có thể sử dụng các API được mô tả trước đó để theo dõi mức độ quan trọng và trạng thái đóng băng của các quy trình ứng dụng:
ActivityManager.addOnUidImportanceListener: Các dịch vụ hệ thống có thể đăng ký một trình nghe để theo dõi các thay đổi về mức độ quan trọng của UID. Khi nhận được lệnh gọi hoặc lệnh gọi lại liên kết từ một ứng dụng, dịch vụ có thể dùngBinder.getCallingUid()để lấy UID và liên kết UID đó với trạng thái quan trọng mà trình nghe theo dõi. Điều này cho phép các dịch vụ hệ thống biết liệu ứng dụng gọi có ở trạng thái được lưu vào bộ nhớ đệm hay không.IBinder.addFrozenStateChangeCallback: Khi một dịch vụ hệ thống nhận được đối tượng liên kết từ một ứng dụng (ví dụ: trong quá trình đăng ký lệnh gọi lại), dịch vụ đó phải đăng ký mộtFrozenStateChangeCallbacktrên phiên bảnIBindercụ thể đó. Thao tác này sẽ thông báo trực tiếp cho dịch vụ hệ thống khi quy trình của ứng dụng lưu trữ liên kết đó bị đóng băng hoặc không bị đóng băng.
Đề xuất cho các dịch vụ hệ thống
Bạn nên tất cả các dịch vụ hệ thống có thể tương tác với ứng dụng đều theo dõi trạng thái được lưu vào bộ nhớ đệm và trạng thái đóng băng của các quy trình ứng dụng mà chúng giao tiếp. Nếu không làm như vậy, bạn có thể:
- Mức tiêu thụ tài nguyên: Việc thực hiện công việc cho các ứng dụng được lưu vào bộ nhớ đệm và không hiển thị cho người dùng có thể lãng phí tài nguyên hệ thống.
- Ứng dụng gặp sự cố: Các lệnh gọi liên kết đồng bộ đến các ứng dụng bị treo sẽ khiến các ứng dụng đó gặp sự cố. Các lệnh gọi liên kết không đồng bộ đến các ứng dụng bị đóng băng sẽ dẫn đến sự cố nếu bộ đệm giao dịch không đồng bộ của các ứng dụng đó bị tràn.
- Hành vi không mong muốn của ứng dụng: Các ứng dụng không bị đóng băng sẽ nhận ngay mọi giao dịch liên kết không đồng bộ được lưu vào bộ nhớ đệm đã gửi cho chúng trong khi chúng bị đóng băng. Các ứng dụng có thể ở trong trạng thái đóng băng trong một khoảng thời gian không xác định, vì vậy, các giao dịch được lưu vào bộ nhớ đệm có thể rất cũ.
Các dịch vụ hệ thống thường dùng RemoteCallbackList để quản lý lệnh gọi lại từ xa và tự động xử lý các quy trình không hoạt động. Để xử lý các ứng dụng bị treo, hãy mở rộng mức sử dụng hiện có của RemoteCallbackList bằng cách áp dụng chính sách về phương thức gọi bị treo như mô tả trong phần Sử dụng RemoteCallbackList.