প্রসেসগুলোর মধ্যে যোগাযোগের জন্য বাইন্ডার ব্যবহার করার সময়, রিমোট প্রসেসটি ক্যাশড বা ফ্রোজেন অবস্থায় থাকলে বিশেষ সতর্কতা অবলম্বন করুন। ক্যাশড বা ফ্রোজেন অ্যাপে কল করলে তা ক্র্যাশ করতে পারে অথবা অপ্রয়োজনীয়ভাবে রিসোর্স খরচ করতে পারে।
ক্যাশ করা এবং হিমায়িত অ্যাপের অবস্থা
অ্যান্ড্রয়েড মেমরি এবং সিপিইউ-এর মতো সিস্টেম রিসোর্স পরিচালনা করার জন্য অ্যাপগুলোকে বিভিন্ন অবস্থায় রাখে।
ক্যাশ করা অবস্থা
যখন কোনো অ্যাপে অ্যাক্টিভিটি বা সার্ভিসের মতো ব্যবহারকারীর কাছে দৃশ্যমান কোনো উপাদান থাকে না, তখন সেটিকে ক্যাশড অবস্থায় নিয়ে যাওয়া যেতে পারে। বিস্তারিত জানতে প্রসেস এবং অ্যাপ লাইফসাইকেল দেখুন। ক্যাশড অ্যাপগুলো মেমরিতে রাখা হয় এই ভেবে যে ব্যবহারকারী হয়তো সেগুলোতে আবার ফিরে আসতে পারে, কিন্তু সেগুলো তখন সক্রিয়ভাবে কাজ করবে বলে আশা করা হয় না।
যখন একটি অ্যাপ প্রসেস থেকে অন্যটিতে বাইন্ডিং করা হয়, যেমন bindService ব্যবহার করে, তখন সার্ভার প্রসেসের স্টেটকে ক্লায়েন্ট প্রসেসের স্টেটের সমান বা তার চেয়ে বেশি গুরুত্বপূর্ণ হিসেবে উন্নীত করা হয় (তবে Context#BIND_WAIVE_PRIORITY নির্দিষ্ট করা থাকলে তা ব্যতিক্রম)। উদাহরণস্বরূপ, যদি ক্লায়েন্ট ক্যাশড স্টেটে না থাকে, তবে সার্ভারও থাকবে না।
বিপরীতভাবে, একটি সার্ভার প্রসেসের অবস্থা তার ক্লায়েন্টদের অবস্থা নির্ধারণ করে না। তাই একটি সার্ভারের ক্লায়েন্টদের সাথে বাইন্ডার সংযোগ থাকতে পারে, যা সাধারণত কলব্যাকের আকারে হয়ে থাকে, এবং যখন রিমোট প্রসেসটি ক্যাশড অবস্থায় থাকে, তখন সার্ভারটি ক্যাশড থাকে না।
এমন এপিআই ডিজাইন করার সময়, যেখানে কলব্যাকগুলো একটি এলিভেটেড প্রসেস থেকে উৎপন্ন হয়ে অ্যাপে পাঠানো হয়, তখন কোনো অ্যাপ ক্যাশড স্টেটে প্রবেশ করলে কলব্যাকের ডিসপ্যাচ থামিয়ে দেওয়ার এবং এই স্টেট থেকে বেরিয়ে এলে তা পুনরায় চালু করার কথা বিবেচনা করুন। এটি ক্যাশড অ্যাপ প্রসেসগুলোতে অপ্রয়োজনীয় কাজ প্রতিরোধ করে।
অ্যাপগুলি কখন ক্যাশড অবস্থায় প্রবেশ করে বা তা থেকে বেরিয়ে আসে তা ট্র্যাক করতে, ActivityManager.addOnUidImportanceListener ব্যবহার করুন:
জাভা
// in ActivityManager or Context
activityManager.addOnUidImportanceListener(
new ActivityManager.OnUidImportanceListener() { ... },
IMPORTANCE_CACHED);
কোটলিন
// in ActivityManager or Context
activityManager.addOnUidImportanceListener({ uid, importance ->
// ...
}, IMPORTANCE_CACHED)
হিমায়িত অবস্থা
সিস্টেম রিসোর্স সাশ্রয়ের জন্য একটি ক্যাশ করা অ্যাপকে ফ্রিজ করতে পারে। যখন একটি অ্যাপ ফ্রিজ করা হয়, তখন এটি শূন্য সিপিইউ টাইম পায় এবং কোনো কাজ করতে পারে না। আরও বিস্তারিত জানতে, ‘ক্যাশ করা অ্যাপ ফ্রিজার’ দেখুন।
যখন কোনো প্রসেস ফ্রোজেন থাকা অন্য কোনো রিমোট প্রসেসে একটি সিনক্রোনাস ( oneway নয়) বাইন্ডার ট্রানজ্যাকশন পাঠায়, তখন সিস্টেম রিমোট প্রসেসটিকে কিল করে দেয়। এর ফলে কলিং প্রসেসের কলিং থ্রেডটি রিমোট প্রসেসটি আনফ্রোজেন হওয়ার জন্য অনির্দিষ্টকালের জন্য আটকে থাকে না, যা কলিং অ্যাপে থ্রেড স্টারভেশন বা ডেডলকের কারণ হতে পারে।
যখন কোনো প্রসেস একটি ফ্রোজেন অ্যাপে একটি অ্যাসিঙ্ক্রোনাস ( oneway ) বাইন্ডার ট্রানজ্যাকশন পাঠায় (সাধারণত একটি কলব্যাক নোটিফাই করার মাধ্যমে, যা সাধারণত একটি oneway মেথড), তখন রিমোট প্রসেসটি আনফ্রোজেন না হওয়া পর্যন্ত ট্রানজ্যাকশনটি বাফার করা থাকে। যদি বাফার ওভারফ্লো হয়, তাহলে প্রাপক অ্যাপ প্রসেসটি ক্র্যাশ করতে পারে। এছাড়াও, অ্যাপ প্রসেসটি আনফ্রোজেন হয়ে ট্রানজ্যাকশনগুলো প্রসেস করার আগেই বাফার করা ট্রানজ্যাকশনগুলো বাসি হয়ে যেতে পারে।
পুরনো ইভেন্ট দিয়ে অ্যাপগুলোকে ভারাক্রান্ত করা বা তাদের বাফার উপচে পড়া এড়াতে, প্রাপক অ্যাপের প্রসেসটি স্থির থাকা অবস্থায় আপনাকে অবশ্যই কলব্যাক প্রেরণ থামিয়ে রাখতে হবে।
অ্যাপ কখন ফ্রিজ বা আনফ্রিজ হয় তা ট্র্যাক করতে 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
}
কোটলিন
// 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++ বাইন্ডার 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 ক্লাসটি রিমোট প্রসেস দ্বারা রেজিস্টার করা IInterface কলব্যাকের তালিকা পরিচালনার জন্য একটি সহায়ক। এই ক্লাসটি স্বয়ংক্রিয়ভাবে বাইন্ডার ডেথ নোটিফিকেশন পরিচালনা করে এবং ফ্রোজেন অ্যাপের কলব্যাক পরিচালনার জন্য বিভিন্ন অপশন প্রদান করে।
RemoteCallbackList তৈরি করার সময়, আপনি একটি ফ্রোজেন ক্যালি পলিসি নির্দিষ্ট করতে পারেন:
-
FROZEN_CALLEE_POLICY_DROP: ফ্রোজেন অ্যাপের কলব্যাকগুলো নীরবে বাদ দেওয়া হয়। এই পলিসিটি তখন ব্যবহার করুন যখন অ্যাপটি ক্যাশড থাকা অবস্থায় ঘটা ইভেন্টগুলো অ্যাপটির জন্য গুরুত্বপূর্ণ নয়, যেমন—রিয়েল-টাইম সেন্সর ইভেন্ট। -
FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT: যদি কোনো অ্যাপ ফ্রিজ থাকা অবস্থায় একাধিক কলব্যাক ব্রডকাস্ট করা হয়, তবে অ্যাপটি আনফ্রিজ হওয়ার পর শুধুমাত্র সর্বশেষ কলব্যাকটি এনকিউ করা হয় এবং ডেলিভার করা হয়। এটি স্টেট-ভিত্তিক কলব্যাকের জন্য উপযোগী, যেখানে শুধুমাত্র সর্বশেষ স্টেট আপডেটটিই গুরুত্বপূর্ণ; উদাহরণস্বরূপ, একটি কলব্যাক যা অ্যাপকে বর্তমান মিডিয়া ভলিউম সম্পর্কে অবহিত করে। -
FROZEN_CALLEE_POLICY_ENQUEUE_ALL: একটি অ্যাপ ফ্রিজ থাকা অবস্থায় ব্রডকাস্ট করা সমস্ত কলব্যাক কিউতে জমা হয় এবং অ্যাপটি আনফ্রিজ হলে ডেলিভার করা হয়। এই পলিসিটি ব্যবহারের ক্ষেত্রে সতর্ক থাকুন, কারণ অতিরিক্ত কলব্যাক কিউতে জমা হলে এটি বাফার ওভারফ্লো ঘটাতে পারে, অথবা পুরনো ইভেন্টগুলো জমা হয়ে যেতে পারে।
নিম্নলিখিত উদাহরণটি দেখায় কিভাবে একটি RemoteCallbackList ইনস্ট্যান্স তৈরি এবং ব্যবহার করতে হয় যা ফ্রোজেন অ্যাপগুলিতে কলব্যাক ড্রপ করে:
জাভা
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));
কোটলিন
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() শুধুমাত্র তখনই কল করে, যখন কলব্যাকটি হোস্টকারী প্রসেসটি ফ্রোজেন (frozen) না থাকে।
সিস্টেম পরিষেবা এবং অ্যাপের মিথস্ক্রিয়া
সিস্টেম সার্ভিসগুলো প্রায়শই বাইন্ডার ব্যবহার করে বিভিন্ন অ্যাপের সাথে যোগাযোগ করে। যেহেতু অ্যাপগুলো ক্যাশড এবং ফ্রোজেন অবস্থায় প্রবেশ করতে পারে, তাই সিস্টেমের স্থিতিশীলতা ও পারফরম্যান্স বজায় রাখতে সিস্টেম সার্ভিসগুলোকে এই যোগাযোগগুলো সুষ্ঠুভাবে পরিচালনা করার জন্য বিশেষ যত্ন নিতে হয়।
সিস্টেম সার্ভিসগুলোকে ইতিমধ্যেই এমন পরিস্থিতি সামলাতে হয় যেখানে বিভিন্ন কারণে অ্যাপ প্রসেস বন্ধ হয়ে যায়। এর মধ্যে রয়েছে তাদের হয়ে কাজ বন্ধ করে দেওয়া এবং বন্ধ হয়ে যাওয়া প্রসেসগুলোতে কলব্যাক পাঠানো চালিয়ে যাওয়ার চেষ্টা না করা। অ্যাপ ফ্রিজ হয়ে যাওয়ার বিষয়টি বিবেচনা করা এই বিদ্যমান পর্যবেক্ষণ দায়িত্বেরই একটি সম্প্রসারণ।
সিস্টেম পরিষেবা থেকে অ্যাপের অবস্থা ট্র্যাক করুন
system_server অথবা নেটিভ ডেমন হিসেবে চলমান সিস্টেম সার্ভিসগুলোও অ্যাপ প্রসেসগুলোর গুরুত্ব এবং ফ্রোজেন অবস্থা ট্র্যাক করার জন্য পূর্বে বর্ণিত API-গুলো ব্যবহার করতে পারে:
ActivityManager.addOnUidImportanceListener: সিস্টেম সার্ভিসগুলো UID-এর গুরুত্বের পরিবর্তন ট্র্যাক করার জন্য একটি লিসেনার রেজিস্টার করতে পারে। কোনো অ্যাপ থেকে বাইন্ডার কল বা কলব্যাক গ্রহণ করার সময়, সার্ভিসটি UID পেতে এবং লিসেনার দ্বারা ট্র্যাক করা গুরুত্বের অবস্থার সাথে সেটিকে মিলিয়ে দেখতেBinder.getCallingUid()ব্যবহার করতে পারে। এর মাধ্যমে সিস্টেম সার্ভিসগুলো জানতে পারে যে কলকারী অ্যাপটি ক্যাশড অবস্থায় আছে কি না।IBinder.addFrozenStateChangeCallback: যখন কোনো সিস্টেম সার্ভিস একটি অ্যাপ থেকে একটি বাইন্ডার অবজেক্ট গ্রহণ করে (উদাহরণস্বরূপ, কলব্যাকের জন্য রেজিস্ট্রেশনের অংশ হিসেবে), তখন সেটির উচিত সেই নির্দিষ্টIBinderইনস্ট্যান্সটিতে একটিFrozenStateChangeCallbackরেজিস্টার করা। এর ফলে, যে অ্যাপ প্রসেসটি ওই বাইন্ডারটিকে হোস্ট করছে, সেটি কখন ফ্রোজেন বা আনফ্রোজেন হয়, তা সরাসরি সিস্টেম সার্ভিসকে জানিয়ে দেয়।
সিস্টেম পরিষেবাগুলির জন্য সুপারিশ
আমরা সুপারিশ করি যে , অ্যাপের সাথে যোগাযোগ করতে পারে এমন সমস্ত সিস্টেম পরিষেবা যেন তাদের সাথে যোগাযোগকারী অ্যাপ প্রসেসগুলির ক্যাশড এবং ফ্রোজেন অবস্থা ট্র্যাক করে । এটি করতে ব্যর্থ হলে নিম্নলিখিত সমস্যাগুলি হতে পারে:
- সম্পদ ব্যবহার: যেসব অ্যাপ ক্যাশ করা থাকে এবং ব্যবহারকারীর কাছে দৃশ্যমান নয়, সেগুলোর জন্য কাজ করলে সিস্টেমের সম্পদ নষ্ট হতে পারে।
- অ্যাপ ক্র্যাশ: ফ্রোজেন অ্যাপে সিনক্রোনাস বাইন্ডার কল করলে তা ক্র্যাশ করে। ফ্রোজেন অ্যাপে অ্যাসিঙ্ক্রোনাস বাইন্ডার কল করলে, সেটির অ্যাসিঙ্ক ট্রানজ্যাকশন বাফার ওভারফ্লো হলে অ্যাপটি ক্র্যাশ করে।
- অ্যাপের অপ্রত্যাশিত আচরণ: আনফ্রোজেন অ্যাপগুলো ফ্রোজেন থাকা অবস্থায় তাদের কাছে পাঠানো যেকোনো বাফার করা অ্যাসিঙ্ক্রোনাস বাইন্ডার ট্রানজ্যাকশন তাৎক্ষণিকভাবে গ্রহণ করে। অ্যাপগুলো অনির্দিষ্টকালের জন্য ফ্রিজারে থাকতে পারে, তাই বাফার করা ট্রানজ্যাকশনগুলো খুব পুরনো বা বাসি হয়ে যেতে পারে।
সিস্টেম সার্ভিসগুলো প্রায়শই রিমোট কলব্যাক পরিচালনা করতে এবং ডেড প্রসেসগুলোকে স্বয়ংক্রিয়ভাবে সামলাতে RemoteCallbackList ব্যবহার করে। ফ্রোজেন অ্যাপগুলো সামলাতে, “Use RemoteCallbackList” অংশে বর্ণিত ফ্রোজেন ক্যালি পলিসি প্রয়োগ করে RemoteCallbackList এর বিদ্যমান ব্যবহারকে প্রসারিত করুন।