प्रोसेस के बीच कम्यूनिकेट करने के लिए, बाइंडर का इस्तेमाल करते समय, इस बात का खास ध्यान रखें कि रिमोट प्रोसेस, कैश मेमोरी में सेव या फ़्रीज़ की गई स्थिति में न हो. कैश मेमोरी में सेव या फ़्रीज़ किए गए ऐप्लिकेशन को कॉल करने पर, वे क्रैश हो सकते हैं या ज़रूरत से ज़्यादा संसाधनों का इस्तेमाल कर सकते हैं.
कैश मेमोरी में सेव और फ़्रीज़ किए गए ऐप्लिकेशन की स्थितियां
Android, सिस्टम के संसाधनों जैसे कि मेमोरी और सीपीयू को मैनेज करने के लिए, ऐप्लिकेशन को अलग-अलग स्थितियों में रखता है.
कैश मेमोरी में सेव होने की स्थिति
जब किसी ऐप्लिकेशन में, उपयोगकर्ता को दिखने वाले कॉम्पोनेंट नहीं होते हैं, जैसे कि गतिविधियां या सेवाएं, तो उसे कैश मेमोरी में सेव होने की स्थिति में ले जाया जा सकता है. ज़्यादा जानकारी के लिए, प्रोसेस और ऐप्लिकेशन का लाइफ़साइकल देखें. कैश मेमोरी में सेव किए गए ऐप्लिकेशन को मेमोरी में रखा जाता है, ताकि उपयोगकर्ता उनके पास वापस जा सके. हालांकि, उनसे सक्रिय रूप से काम करने की उम्मीद नहीं की जाती.
जब bindService का इस्तेमाल करके, किसी एक ऐप्लिकेशन की प्रोसेस को दूसरी प्रोसेस से बाइंड किया जाता है, तो सर्वर प्रोसेस की स्थिति, क्लाइंट प्रोसेस जितनी ही अहम हो जाती है. हालांकि, ऐसा तब नहीं होता, जब Context#BIND_WAIVE_PRIORITY तय किया जाता है. उदाहरण के लिए, अगर क्लाइंट, कैश मेमोरी में सेव होने की स्थिति में नहीं है, तो सर्वर भी नहीं होगा.
इसके उलट, सर्वर प्रोसेस की स्थिति से, उसके क्लाइंट की स्थिति तय नहीं होती. इसलिए, किसी सर्वर के पास क्लाइंट के साथ बाइंडर कनेक्शन हो सकते हैं. आम तौर पर, ये कनेक्शन कॉलबैक के तौर पर होते हैं. साथ ही, रिमोट प्रोसेस, कैश मेमोरी में सेव होने की स्थिति में होती है, लेकिन सर्वर, कैश मेमोरी में सेव नहीं होता.
एपीआई डिज़ाइन करते समय, इस बात का ध्यान रखें कि कॉलबैक, ज़्यादा अधिकारों वाली प्रोसेस से शुरू होते हैं और ऐप्लिकेशन को डिलीवर किए जाते हैं. साथ ही, जब कोई ऐप्लिकेशन, कैश मेमोरी में सेव होने की स्थिति में आता है, तो कॉलबैक को भेजना रोक दें. जब ऐप्लिकेशन इस स्थिति से बाहर आता है, तो कॉलबैक को भेजना फिर से शुरू करें. इससे, कैश मेमोरी में सेव किए गए ऐप्लिकेशन की प्रोसेस में, ज़रूरत से ज़्यादा काम नहीं होता.
यह ट्रैक करने के लिए कि ऐप्लिकेशन, कैश मेमोरी में सेव होने की स्थिति में कब आते हैं या इस स्थिति से कब बाहर निकलते हैं, 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)
फ़्रीज़ होने की स्थिति
सिस्टम, संसाधनों को बचाने के लिए, कैश मेमोरी में सेव किए गए ऐप्लिकेशन को फ़्रीज़ कर सकता है. जब कोई ऐप्लिकेशन फ़्रीज़ होता है, तो उसे सीपीयू का समय नहीं मिलता. साथ ही, वह कोई काम नहीं कर पाता. ज़्यादा जानकारी के लिए, कैश मेमोरी में सेव किए गए ऐप्लिकेशन को फ़्रीज़ करने की सुविधा देखें.
जब कोई प्रोसेस, फ़्रीज़ की गई किसी दूसरी रिमोट प्रोसेस को सिंक्रोनस (न कि oneway) बाइंडर ट्रांज़ैक्शन भेजती है, तो सिस्टम, रिमोट प्रोसेस को बंद कर देता है. इससे, कॉल करने वाली प्रोसेस में मौजूद कॉल करने वाला थ्रेड, रिमोट प्रोसेस के अनफ़्रीज़ होने का इंतज़ार करते समय, हमेशा के लिए हैंग नहीं होता. इससे, कॉल करने वाले ऐप्लिकेशन में थ्रेड स्टारवेशन या डेडलॉक हो सकते हैं.
जब कोई प्रोसेस, फ़्रीज़ किए गए ऐप्लिकेशन को एसिंक्रोनस (oneway) बाइंडर ट्रांज़ैक्शन भेजती है, तो आम तौर पर, कॉलबैक की सूचना देकर, जो आम तौर पर oneway तरीका होता है, ट्रांज़ैक्शन को तब तक बफ़र किया जाता है, जब तक रिमोट प्रोसेस अनफ़्रीज़ नहीं हो जाती. अगर बफ़र ओवरफ़्लो होता है, तो पाने वाले ऐप्लिकेशन की प्रोसेस क्रैश हो सकती है. इसके अलावा, बफ़र किए गए ट्रांज़ैक्शन, ऐप्लिकेशन की प्रोसेस के अनफ़्रीज़ होने और उन्हें प्रोसेस करने तक पुराने हो सकते हैं.
ऐप्लिकेशन को पुराने इवेंट से बचाने या उनके बफ़र को ओवरफ़्लो होने से बचाने के लिए, पाने वाले ऐप्लिकेशन की प्रोसेस के फ़्रीज़ होने के दौरान, कॉलबैक को भेजना ज़रूर रोकें.
यह ट्रैक करने के लिए कि ऐप्लिकेशन कब फ़्रीज़ होते हैं या अनफ़्रीज़ होते हैं, इस्तेमाल करें
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++ बाइंडर एपीआई का इस्तेमाल करने वाले प्लैटफ़ॉर्म कोड के लिए:
#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 क्लास,
IInterface कॉलबैक की सूचियों को मैनेज करने में मदद करती है. इन सूचियों को रिमोट प्रोसेस रजिस्टर करती हैं. यह क्लास, बाइंडर के बंद होने की सूचनाओं को अपने-आप मैनेज करती है. साथ ही, फ़्रीज़ किए गए ऐप्लिकेशन को कॉलबैक मैनेज करने के विकल्प उपलब्ध कराती है.
RemoteCallbackList बनाते समय, फ़्रीज़ किए गए कैली की नीति तय की जा सकती है:
FROZEN_CALLEE_POLICY_DROP: फ़्रीज़ किए गए ऐप्लिकेशन को कॉलबैक, बिना किसी सूचना के ड्रॉप कर दिए जाते हैं. इस नीति का इस्तेमाल तब करें, जब ऐप्लिकेशन के कैश मेमोरी में सेव होने के दौरान हुए इवेंट, ऐप्लिकेशन के लिए अहम न हों. उदाहरण के लिए, रीयल-टाइम सेंसर इवेंट.FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT: अगर किसी ऐप्लिकेशन के फ़्रीज़ होने के दौरान, कई कॉलबैक ब्रॉडकास्ट किए जाते हैं, तो सिर्फ़ सबसे नया कॉलबैक, एनक्यू किया जाता है. साथ ही, ऐप्लिकेशन के अनफ़्रीज़ होने पर उसे डिलीवर किया जाता है. यह नीति, स्थिति के आधार पर कॉलबैक के लिए काम की है. इनमें सिर्फ़ सबसे नए स्थिति अपडेट की अहमियत होती है. उदाहरण के लिए, ऐप्लिकेशन को मौजूदा मीडिया वॉल्यूम की सूचना देने वाला कॉलबैक.FROZEN_CALLEE_POLICY_ENQUEUE_ALL: किसी ऐप्लिकेशन के फ़्रीज़ होने के दौरान ब्रॉडकास्ट किए गए सभी कॉलबैक, एनक्यू किए जाते हैं. साथ ही, ऐप्लिकेशन के अनफ़्रीज़ होने पर उन्हें डिलीवर किया जाता है. इस नीति का इस्तेमाल करते समय सावधानी बरतें, क्योंकि अगर बहुत ज़्यादा कॉलबैक एनक्यू किए जाते हैं, तो बफ़र ओवरफ़्लो हो सकता है. साथ ही, पुराने इवेंट इकट्ठा हो सकते हैं.
यहां दिए गए उदाहरण में, 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() को सिर्फ़ तब लागू करता है, जब कॉलबैक को होस्ट करने वाली प्रोसेस फ़्रीज़ न हो.
सिस्टम की सेवाएं और ऐप्लिकेशन के इंटरैक्शन
सिस्टम की सेवाएं, अक्सर बाइंडर का इस्तेमाल करके, कई अलग-अलग ऐप्लिकेशन के साथ इंटरैक्ट करती हैं. ऐप्लिकेशन, कैश मेमोरी में सेव और फ़्रीज़ होने की स्थिति में आ सकते हैं. इसलिए, सिस्टम की सेवाओं को इन इंटरैक्शन को आसानी से मैनेज करने के लिए, खास ध्यान रखना चाहिए. इससे, सिस्टम की स्थिरता और परफ़ॉर्मेंस को बनाए रखने में मदद मिलती है.
सिस्टम की सेवाओं को पहले से ही उन स्थितियों को मैनेज करना होता है जिनमें ऐप्लिकेशन की प्रोसेस, अलग-अलग वजहों से बंद हो जाती हैं. इसमें, उनकी ओर से काम करना बंद करना और बंद हो चुकी प्रोसेस को कॉलबैक डिलीवर करने की कोशिश न करना शामिल है. ऐप्लिकेशन के फ़्रीज़ होने की स्थिति को ध्यान में रखना, निगरानी की मौजूदा ज़िम्मेदारी का ही हिस्सा है.
सिस्टम की सेवाओं से ऐप्लिकेशन की स्थितियों को ट्रैक करना
system_server में या नेटिव डेमॉन के तौर पर चलने वाली सिस्टम की सेवाएं, ऐप्लिकेशन की प्रोसेस की अहमियत और फ़्रीज़ होने की स्थिति को ट्रैक करने के लिए, पहले बताए गए एपीआई का इस्तेमाल भी कर सकती हैं:
ActivityManager.addOnUidImportanceListener: सिस्टम की सेवाएं, यूआईडी की अहमियत में होने वाले बदलावों को ट्रैक करने के लिए, लिसनर रजिस्टर कर सकती हैं. किसी ऐप्लिकेशन से बाइंडर कॉल या कॉलबैक मिलने पर, सेवा, यूआईडी पाने के लिएBinder.getCallingUid()का इस्तेमाल कर सकती है. साथ ही, इसे लिसनर से ट्रैक की गई अहमियत की स्थिति से जोड़ा जा सकता है. इससे, सिस्टम की सेवाओं को पता चलता है कि कॉल करने वाला ऐप्लिकेशन, कैश मेमोरी में सेव होने की स्थिति में है या नहीं.IBinder.addFrozenStateChangeCallback: जब किसी सिस्टम सेवा को किसी ऐप्लिकेशन से बाइंडर ऑब्जेक्ट मिलता है, तो उदाहरण के लिए, कॉलबैक के लिए रजिस्ट्रेशन के तौर पर, उसे उस खासIBinderइंस्टेंस परFrozenStateChangeCallbackरजिस्टर करना चाहिए. इससे, सिस्टम सेवा को सीधे सूचना मिलती है कि उस बाइंडर को होस्ट करने वाली ऐप्लिकेशन की प्रोसेस, फ़्रीज़ हो गई है या अनफ़्रीज़ हो गई है.
सिस्टम की सेवाओं के लिए सुझाव
हमारा सुझाव है कि ऐप्लिकेशन के साथ इंटरैक्ट करने वाली सभी सिस्टम की सेवाएं, उन ऐप्लिकेशन की प्रोसेस की कैश मेमोरी में सेव और फ़्रीज़ होने की स्थिति को ट्रैक करें जिनसे वे कम्यूनिकेट करती हैं. ऐसा न करने पर, ये समस्याएं हो सकती हैं:
- संसाधनों का इस्तेमाल: कैश मेमोरी में सेव किए गए और उपयोगकर्ता को न दिखने वाले ऐप्लिकेशन के लिए काम करने से, सिस्टम के संसाधन बर्बाद हो सकते हैं.
- ऐप्लिकेशन क्रैश होना: फ़्रीज़ किए गए ऐप्लिकेशन को सिंक्रोनस बाइंडर कॉल करने पर, वे क्रैश हो जाते हैं. फ़्रीज़ किए गए ऐप्लिकेशन को एसिंक्रोनस बाइंडर कॉल करने पर, अगर उनका एसिंक ट्रांज़ैक्शन बफ़र ओवरफ़्लो होता है, तो वे क्रैश हो जाते हैं.
- ऐप्लिकेशन का अनचाहा व्यवहार: अनफ़्रीज़ किए गए ऐप्लिकेशन को, बफ़र किए गए सभी एसिंक्रोनस बाइंडर ट्रांज़ैक्शन तुरंत मिल जाते हैं. ये ट्रांज़ैक्शन, ऐप्लिकेशन के फ़्रीज़ होने के दौरान भेजे गए थे. ऐप्लिकेशन, फ़्रीज़ होने की स्थिति में हमेशा के लिए रह सकते हैं. इसलिए, बफ़र किए गए ट्रांज़ैक्शन पुराने हो सकते हैं.
सिस्टम की सेवाएं, अक्सर RemoteCallbackList को मैनेज करने और बंद हो चुकी प्रोसेस को अपने-आप मैनेज करने के लिए, रिमोट कॉलबैक
का इस्तेमाल करती हैं. फ़्रीज़ किए गए ऐप्लिकेशन को मैनेज करने के लिए,
मौजूदा इस्तेमाल को बढ़ाएं. इसके लिए, फ़्रीज़ किए गए कैली की नीति लागू करें.
इसके बारे में, RemoteCallbackList का इस्तेमाल करना में बताया गया है.RemoteCallbackList