เมื่อใช้ Binder เพื่อสื่อสารระหว่างกระบวนการ ให้ระมัดระวังเป็นพิเศษเมื่อกระบวนการระยะไกลอยู่ในสถานะแคชหรือสถานะหยุดทำงาน การโทรเข้าแอปที่อยู่ในสถานะแคชหรือสถานะหยุดทำงานอาจทำให้แอปขัดข้องหรือใช้ทรัพยากรโดยไม่จำเป็น
สถานะแคชและสถานะหยุดทำงานของแอป
Android จะเก็บแอปไว้ในสถานะต่างๆ เพื่อจัดการทรัพยากรของระบบ เช่น หน่วยความจำและ CPU
สถานะแคช
เมื่อแอปไม่มีคอมโพเนนต์ที่ผู้ใช้มองเห็นได้ เช่น กิจกรรมหรือบริการ ระบบจะย้ายแอปไปยังสถานะ แคช ดูรายละเอียดได้ที่กระบวนการและวงจรชีวิตของแอปสำหรับ รายละเอียด ระบบจะเก็บแอปที่แคชไว้ในหน่วยความจำในกรณีที่ผู้ใช้กลับไปใช้แอปเหล่านั้น แต่แอปที่แคชไว้จะไม่ทำงานอย่างต่อเนื่อง
เมื่อผูกจากกระบวนการของแอปหนึ่งไปยังอีกกระบวนการหนึ่ง เช่น การใช้ bindService,
สถานะกระบวนการของกระบวนการเซิร์ฟเวอร์จะได้รับการยกระดับให้มีความสำคัญอย่างน้อยเท่ากับ
กระบวนการไคลเอ็นต์ (ยกเว้นเมื่อมีการระบุ Context#BIND_WAIVE_PRIORITY)
เช่น หากไคลเอ็นต์ไม่ได้อยู่ในสถานะแคช เซิร์ฟเวอร์ก็จะไม่แคชเช่นกัน
ในทางกลับกัน สถานะของกระบวนการเซิร์ฟเวอร์ไม่ได้กำหนดสถานะของไคลเอ็นต์ ดังนั้น เซิร์ฟเวอร์อาจมีการเชื่อมต่อ Binder กับไคลเอ็นต์ ซึ่งส่วนใหญ่จะอยู่ในรูปแบบของการเรียกกลับ และในขณะที่กระบวนการระยะไกลอยู่ในสถานะแคช เซิร์ฟเวอร์จะไม่แคช
เมื่อออกแบบ API ที่การเรียกกลับมาจากกระบวนการที่ได้รับการยกระดับและส่งไปยังแอป ให้พิจารณาหยุดการส่งการเรียกกลับชั่วคราวเมื่อแอปเข้าสู่สถานะแคช และกลับมาส่งต่อเมื่อแอปออกจากสถานะนี้ ซึ่งจะช่วยป้องกันไม่ให้กระบวนการของแอปที่แคชไว้ทำงานโดยไม่จำเป็น
หากต้องการติดตามเวลาที่แอปเข้าสู่หรือออกจากสถานะแคช ให้ใช้ ActivityManager.addOnUidImportanceListener ดังนี้
Java
// in ActivityManager or Context
activityManager.addOnUidImportanceListener(
new ActivityManager.OnUidImportanceListener() { ... },
IMPORTANCE_CACHED);
Kotlin
// in ActivityManager or Context
activityManager.addOnUidImportanceListener({ uid, importance ->
// ...
}, IMPORTANCE_CACHED)
สถานะหยุดทำงาน
ระบบสามารถ หยุดทำงาน แอปที่แคชไว้เพื่อประหยัดทรัพยากร เมื่อแอปหยุดทำงาน แอปจะได้รับเวลา CPU เป็น 0 และไม่สามารถทำงานใดๆ ได้ ดูรายละเอียดเพิ่มเติมได้ที่ ตัวหยุดทำงานของแอปที่แคชไว้
เมื่อกระบวนการส่งธุรกรรม Binder แบบซิงโครนัส (ไม่ใช่ oneway) ไปยังกระบวนการระยะไกลอื่นที่หยุดทำงาน ระบบจะหยุดกระบวนการระยะไกล ซึ่งจะช่วยป้องกันไม่ให้เธรดการเรียกในกระบวนการเรียกค้างอย่างไม่มีกำหนดขณะรอให้กระบวนการระยะไกลกลับมาทำงาน ซึ่งอาจทำให้เกิดการขาดแคลนเธรดหรือการติดตายในแอปที่เรียก
เมื่อกระบวนการส่งธุรกรรม Binder แบบอะซิงโครนัส (oneway) ไปยังแอปที่หยุดทำงาน (โดยทั่วไปคือการแจ้งการเรียกกลับ ซึ่งมักจะเป็นเมธอด oneway) ระบบจะบัฟเฟอร์ธุรกรรมไว้จนกว่ากระบวนการระยะไกลจะกลับมาทำงาน หากบัฟเฟอร์ล้น กระบวนการของแอปผู้รับอาจขัดข้อง นอกจากนี้ ธุรกรรมที่บัฟเฟอร์ไว้อาจล้าสมัยเมื่อกระบวนการของแอปกลับมาทำงานและประมวลผลธุรกรรมเหล่านั้น
หากต้องการหลีกเลี่ยงไม่ให้แอปได้รับเหตุการณ์ที่ล้าสมัยมากเกินไปหรือบัฟเฟอร์ล้น คุณต้อง หยุดส่ง Callback ชั่วคราวขณะที่กระบวนการของแอปผู้รับหยุดทำงาน
หากต้องการติดตามเวลาที่แอปหยุดทำงานหรือกลับมาทำงาน ให้ใช้
IBinder.addFrozenStateChangeCallback
Java
// 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
}
Kotlin
// The binder token of the remote process
val binder: IBinder = service.getBinder()
// Keep track of frozen state
val remoteFrozen = AtomicBoolean(false)
// Update remoteFrozen when the remote process freezes or unfreezes
binder.addFrozenStateChangeCallback(myExecutor) { isFrozen ->
remoteFrozen.set(isFrozen)
}
// When dispatching callbacks to the remote process, pause dispatch if frozen:
if (!remoteFrozen.get()) {
// dispatch callback to remote process
}
C++
สำหรับโค้ดแพลตฟอร์มที่ใช้ C++ Binder API
#include <binder/Binder.h>
#include <binder/IBinder.h>
// The binder token of the remote process
android::sp<android::IBinder> binder = service->getBinder();
// Keep track of frozen state
std::atomic<bool> remoteFrozen = false;
// Define a callback class
class MyFrozenStateCallback : public android::IBinder::FrozenStateChangeCallback {
public:
explicit MyFrozenStateCallback(std::atomic<bool>* frozenState) : mFrozenState(frozenState) {}
void onFrozenStateChanged(bool isFrozen) override {
mFrozenState->store(isFrozen);
}
private:
std::atomic<bool>* mFrozenState;
};
// Update remoteFrozen when the remote process freezes or unfreezes
if (binder != nullptr) {
binder->addFrozenStateChangeCallback(android::sp<android::IBinder::FrozenStateChangeCallback>::make(
new MyFrozenStateCallback(&remoteFrozen)));
}
// When dispatching callbacks to the remote process, pause dispatch if frozen:
if (!remoteFrozen.load()) {
// dispatch callback to remote process
}
ใช้ RemoteCallbackList
คลาส RemoteCallbackList เป็นคลาสตัวช่วยสำหรับการจัดการรายการ Callback
IInterface ที่กระบวนการระยะไกลลงทะเบียนไว้ คลาสนี้จะจัดการการแจ้งเตือนการสิ้นสุดของ Binder โดยอัตโนมัติและมีตัวเลือกสำหรับการจัดการการเรียกกลับไปยังแอปที่หยุดทำงาน
เมื่อสร้าง RemoteCallbackList คุณสามารถระบุนโยบายผู้รับที่หยุดทำงานได้ดังนี้
FROZEN_CALLEE_POLICY_DROP: ระบบจะละทิ้งการเรียกกลับไปยังแอปที่หยุดทำงานโดยไม่มีการแจ้งเตือน ใช้นโยบายนี้เมื่อเหตุการณ์ที่เกิดขึ้นขณะที่แอปแคชไว้ไม่สำคัญต่อแอป เช่น เหตุการณ์เซ็นเซอร์แบบเรียลไทม์FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT: หากมีการออกอากาศการเรียกกลับหลายรายการขณะที่แอปหยุดทำงาน ระบบจะจัดคิวและส่งเฉพาะรายการล่าสุดเมื่อแอปกลับมาทำงาน นโยบายนี้มีประโยชน์สำหรับการเรียกกลับตามสถานะที่การอัปเดตสถานะล่าสุดเท่านั้นที่สำคัญ เช่น การเรียกกลับที่แจ้งให้แอปทราบถึงระดับเสียงสื่อปัจจุบันFROZEN_CALLEE_POLICY_ENQUEUE_ALL: ระบบจะจัดคิวและส่งการเรียกกลับทั้งหมดที่ออกอากาศขณะที่แอปหยุดทำงานเมื่อแอปกลับมาทำงาน โปรดใช้ความระมัดระวังกับนโยบายนี้ เนื่องจากอาจทำให้บัฟเฟอร์ล้นหากมีการจัดคิว Callback มากเกินไป หรือทำให้เกิดเหตุการณ์ที่ล้าสมัยสะสม
ตัวอย่างต่อไปนี้แสดงวิธีสร้างและใช้อินสแตนซ์ RemoteCallbackList ที่ละทิ้งการเรียกกลับไปยังแอปที่หยุดทำงาน
Java
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));
Kotlin
val callbacks =
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) }
หากคุณใช้ FROZEN_CALLEE_POLICY_DROP ระบบจะเรียกใช้ callback.onSomeEvent() ก็ต่อเมื่อกระบวนการที่โฮสต์การเรียกกลับไม่ได้หยุดทำงาน
บริการของระบบและการโต้ตอบกับแอป
บริการของระบบมักจะโต้ตอบกับแอปต่างๆ มากมายโดยใช้ Binder เนื่องจากแอปสามารถเข้าสู่สถานะแคชและสถานะหยุดทำงานได้ บริการของระบบจึงต้องระมัดระวังเป็นพิเศษในการจัดการการโต้ตอบเหล่านี้อย่างเหมาะสมเพื่อช่วยรักษาเสถียรภาพและประสิทธิภาพของระบบ
บริการของระบบต้องจัดการสถานการณ์ที่กระบวนการของแอปถูกหยุดด้วยเหตุผลต่างๆ อยู่แล้ว ซึ่งรวมถึงการหยุดทำงานในนามของแอปและการไม่พยายามส่งการเรียกกลับไปยังกระบวนการที่สิ้นสุดแล้ว การพิจารณาว่าแอปหยุดทำงานหรือไม่เป็นการขยายความรับผิดชอบในการตรวจสอบที่มีอยู่
ติดตามสถานะแอปจากบริการของระบบ
บริการของระบบที่ทำงานใน system_server หรือเป็นเดมอนเนทีฟยังสามารถใช้ API ที่อธิบายไว้ก่อนหน้านี้เพื่อติดตามความสำคัญและสถานะหยุดทำงานของกระบวนการของแอปได้ด้วย ดังนี้
ActivityManager.addOnUidImportanceListener: บริการของระบบสามารถลงทะเบียน Listener เพื่อติดตามการเปลี่ยนแปลงความสำคัญของ UID เมื่อได้รับ Binder Call หรือการเรียกกลับจากแอป บริการจะใช้Binder.getCallingUid()เพื่อรับ UID และเชื่อมโยงกับสถานะความสำคัญ ที่ Listener ติดตาม ซึ่งจะช่วยให้บริการของระบบทราบว่าแอปที่เรียกอยู่ในสถานะแคชหรือไม่IBinder.addFrozenStateChangeCallback: เมื่อบริการของระบบ ได้รับออบเจ็กต์ Binder จากแอป (เช่น เป็นส่วนหนึ่งของการลงทะเบียน เพื่อรับการเรียกกลับ) บริการควรลงทะเบียนFrozenStateChangeCallbackใน อินสแตนซ์IBinderที่เฉพาะเจาะจง ซึ่งจะแจ้งให้บริการของระบบทราบโดยตรงเมื่อกระบวนการของแอปที่โฮสต์ Binder นั้นหยุดทำงานหรือกลับมาทำงาน
คำแนะนำสำหรับบริการของระบบ
เราขอแนะนำให้บริการของระบบทั้งหมดที่อาจโต้ตอบกับแอปติดตามสถานะแคชและสถานะหยุดทำงาน ของกระบวนการของแอปที่บริการสื่อสารด้วย หากไม่ทำเช่นนั้นอาจทำให้เกิดปัญหาต่อไปนี้
- การใช้ทรัพยากร: การทำงานสำหรับแอปที่แคชไว้และผู้ใช้มองไม่เห็นอาจทำให้สิ้นเปลืองทรัพยากรของระบบ
- แอปขัดข้อง: Binder Call แบบซิงโครนัสไปยังแอปที่หยุดทำงานจะทำให้แอปขัดข้อง Binder Call แบบอะซิงโครนัสไปยังแอปที่หยุดทำงานจะทำให้แอปขัดข้องหากบัฟเฟอร์ธุรกรรมแบบอะซิงโครนัสล้น
- ลักษณะการทำงานที่ไม่คาดคิดของแอป: แอปที่กลับมาทำงานจะได้รับธุรกรรม Binder แบบอะซิงโครนัสที่บัฟเฟอร์ไว้ซึ่งส่งไปยังแอปขณะที่แอปหยุดทำงานทันที แอปอาจอยู่ในสถานะหยุดทำงานเป็นระยะเวลาไม่จำกัด ดังนั้นธุรกรรมที่บัฟเฟอร์ไว้อาจล้าสมัยมาก
บริการของระบบมักจะใช้ RemoteCallbackList เพื่อจัดการการเรียกกลับระยะไกล
และจัดการกระบวนการที่สิ้นสุดแล้วโดยอัตโนมัติ หากต้องการจัดการแอปที่หยุดทำงาน ให้ขยายการใช้งาน
RemoteCallbackListที่มีอยู่โดยใช้นโยบายผู้รับที่หยุดทำงาน
ตามที่อธิบายไว้ในใช้ RemoteCallbackList