ক্যাশে ও ফ্রিজ করা অ্যাপ ম্যানেজ করা

প্রসেসের মধ্যে যোগাযোগ করার জন্য বাইন্ডার ব্যবহার করার সময়, রিমোট প্রসেস ক্যাশে করা বা ফ্রিজ করা অবস্থায় থাকলে বিশেষ যত্ন নিন। ক্যাশে করা বা ফ্রোজেন অ্যাপে কল করলে সেগুলি ক্র্যাশ করতে পারে বা অপ্রয়োজনীয় রিসোর্স ব্যবহার করতে পারে।

ক্যাশে করা ও ফ্রোজেন অ্যাপের স্ট্যাটাস

মেমরি ও সিপিইউ-এর মতো সিস্টেম রিসোর্স ম্যানেজ করতে Android বিভিন্ন স্টেটে অ্যাপ ম্যানেজ করে।

ক্যাশে করা স্ট্যাটাস

কোনও অ্যাপে অ্যাক্টিভিটি বা পরিষেবার মতো ব্যবহারকারীর কাছে দৃশ্যমান কম্পোনেন্ট না থাকলে, সেটি ক্যাশে করা অবস্থায় সরানো যেতে পারে। বিস্তারিত জানতে প্রসেস ও অ্যাপ লাইফসাইকেল দেখুন। ব্যবহারকারী যদি আবার অ্যাপে ফিরে আসেন, সেই জন্য ক্যাশে করা অ্যাপ মেমরিতে রাখা হয়, তবে সেগুলি অ্যাক্টিভভাবে কাজ করবে বলে আশা করা হয় না।

একটি অ্যাপ প্রসেস থেকে অন্যটিতে বাইন্ড করার সময়, যেমন bindService ব্যবহার করা, সার্ভার প্রসেসের প্রসেস স্টেটকে অন্তত ক্লায়েন্ট প্রসেসের মতো গুরুত্বপূর্ণ হিসেবে উন্নত করা হয় (Context#BIND_WAIVE_PRIORITY নির্দিষ্ট করা থাকলে ছাড়া)। যেমন, ক্লায়েন্ট ক্যাশেড অবস্থায় না থাকলে, সার্ভারও থাকে না।

অন্যদিকে, কোনও সার্ভার প্রসেসের স্টেট তার ক্লায়েন্টদের স্টেট নির্ধারণ করে না। তাই, কোনও সার্ভারের ক্লায়েন্টের সাথে বাইন্ডার কানেকশন থাকতে পারে, সাধারণত কলব্যাকের আকারে এবং রিমোট প্রসেস ক্যাশে করা অবস্থায় থাকলেও, সার্ভার ক্যাশে করা থাকে না।

এমন API ডিজাইন করার সময় যেখানে কলব্যাক কোনও উন্নত প্রসেস থেকে তৈরি হয় এবং অ্যাপে ডেলিভার করা হয়, কোনও অ্যাপ ক্যাশেড স্টেটে প্রবেশ করলে কলব্যাক ডিসপ্যাচ করা পজ করার কথা বিবেচনা করুন এবং এটি এই স্টেট থেকে বেরিয়ে এলে আবার চালু করুন। এটি ক্যাশে করা অ্যাপ প্রসেসে অপ্রয়োজনীয় কাজ প্রতিরোধ করে।

কখন অ্যাপ ক্যাশেড স্টেটে প্রবেশ করে বা বেরিয়ে আসে তা ট্র্যাক করতে, ActivityManager.addOnUidImportanceListener ব্যবহার করুন:

জাভা

// 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:

জাভা

// 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++ বাইন্ডার 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 ক্লাস হল রিমোট প্রসেস রেজিস্টার করে এমন 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));

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-এ বা নেটিভ ডেমনের মতো রান করা সিস্টেম পরিষেবাগুলিও অ্যাপ প্রসেসের গুরুত্ব ও ফ্রোজেন স্টেট ট্র্যাক করতে আগে বর্ণনা করা API ব্যবহার করতে পারে:

  • ActivityManager.addOnUidImportanceListener: UID-এর গুরুত্ব সংক্রান্ত পরিবর্তন ট্র্যাক করতে সিস্টেম পরিষেবা লিসনার রেজিস্টার করতে পারে। কোনও অ্যাপ থেকে বাইন্ডার কল বা কলব্যাক পেলে, পরিষেবাটি UID পেতে Binder.getCallingUid() ব্যবহার করতে পারে এবং এটিকে লিসনারের ট্র্যাক করা গুরুত্বের স্ট্যাটাসের সাথে কোরিলেট করতে পারে। এর ফলে সিস্টেম পরিষেবা জানতে পারে যে কলিং অ্যাপ ক্যাশে করা অবস্থায় আছে কিনা।

  • IBinder.addFrozenStateChangeCallback: কোনও সিস্টেম পরিষেবা যখন কোনও অ্যাপ থেকে বাইন্ডার অবজেক্ট পায় (যেমন, কলব্যাকের জন্য রেজিস্ট্রেশনের অংশ হিসেবে), তখন সেটিকে সেই নির্দিষ্ট IBinder ইনস্ট্যান্সে FrozenStateChangeCallback রেজিস্টার করতে হবে। সেই বাইন্ডার হোস্ট করা অ্যাপ প্রসেস ফ্রিজ বা আনফ্রিজ হয়ে গেলে, এটি সরাসরি সিস্টেম সার্ভিসকে বিজ্ঞপ্তি পাঠায়।

সিস্টেম পরিষেবার জন্য সাজেশন

আমরা সাজেস্ট করি যে অ্যাপের সাথে ইন্টার‍্যাক্ট করতে পারে এমন সব সিস্টেম পরিষেবা সেগুলির সাথে যোগাযোগ করা অ্যাপ প্রসেসের ক্যাশে ও ফ্রিজ করা স্টেট ট্র্যাক করে। এটি না করলে এগুলি হতে পারে:

  • রিসোর্স খরচ: ক্যাশে করা এবং ব্যবহারকারী দেখতে পান না এমন অ্যাপের জন্য কাজ করলে সিস্টেম রিসোর্স নষ্ট হতে পারে।
  • অ্যাপ ক্র্যাশ করা: ফ্রোজেন অ্যাপে সিঙ্ক্রোনাস বাইন্ডার কল করলে সেগুলি ক্র্যাশ করে। ফ্রিজ করা অ্যাপে অ্যাসিঙ্ক্রোনাস বাইন্ডার কল করলে, সেটির async ট্রানজ্যাকশন বাফার ওভারফ্লো হয়ে যায় এবং ক্র্যাশ করে।
  • অ্যাপের অপ্রত্যাশিত আচরণ: আনফ্রিজ করা অ্যাপগুলি অবিলম্বে কোনও বাফার করা অ্যাসিঙ্ক্রোনাস বাইন্ডার ট্রানজ্যাকশন পায় যা সেগুলি ফ্রিজ থাকাকালীন তাদের পাঠানো হয়েছিল। অ্যাপ ফ্রিজারে অনির্দিষ্টকালের জন্য থাকতে পারে, তাই বাফার করা ট্রানজ্যাকশন অনেক পুরনো হতে পারে।

সিস্টেম সার্ভিস প্রায়ই রিমোট কলব্যাক ম্যানেজ করতে এবং ডেড প্রসেস অটোমেটিক ম্যানেজ করতে RemoteCallbackList ব্যবহার করে। জমে যাওয়া অ্যাপ ম্যানেজ করতে, RemoteCallbackList-এর বর্তমান ব্যবহার বাড়ান। এর জন্য Use RemoteCallbackList-এ বর্ণিত জমে যাওয়া কলার নীতি প্রয়োগ করুন।