التعامل مع التطبيقات المخزَّنة مؤقتًا والمجمَّدة

عند استخدام أداة الربط للتواصل بين العمليات، يجب توخّي الحذر بشكل خاص عندما تكون العملية البعيدة في حالة مخزّنة مؤقتًا أو مجمّدة. قد يؤدي إجراء مكالمات في التطبيقات المخزّنة مؤقتًا أو المجمدة إلى تعطّلها أو استهلاك الموارد بدون داعٍ.

حالات التطبيقات المخزّنة مؤقتًا والمجمّدة

يحافظ نظام التشغيل Android على التطبيقات في حالات مختلفة لإدارة موارد النظام، مثل الذاكرة ووحدة المعالجة المركزية.

الحالة المخزَّنة مؤقتًا

عندما لا يحتوي التطبيق على أي مكوّنات مرئية للمستخدم، مثل الأنشطة أو الخدمات، يمكن نقله إلى حالة التخزين المؤقت. اطّلِع على العمليات ودورة حياة التطبيق للحصول على التفاصيل. يتم الاحتفاظ بالتطبيقات المخزّنة مؤقتًا في الذاكرة في حال عاد المستخدم إليها، ولكن لا يُتوقّع أن تعمل بشكل نشط.

عند الربط من عملية تطبيق إلى أخرى، مثل استخدام bindService، يتم رفع حالة عملية الخادم لتكون على الأقل بنفس أهمية عملية العميل (إلا عند تحديد Context#BIND_WAIVE_PRIORITY). على سبيل المثال، إذا لم يكن العميل في حالة التخزين المؤقت، لن يكون الخادم كذلك.

في المقابل، لا تحدّد حالة عملية الخادم حالة عملائه. لذا، قد يكون للخادم اتصالات binder بالعملاء، وعادةً ما تكون على شكل عمليات ردّ الاتصال، وبينما تكون العملية البعيدة في حالة التخزين المؤقت، لا يتم تخزين الخادم مؤقتًا.

عند تصميم واجهات برمجة تطبيقات تنشأ فيها دوال رد النداء في عملية ذات امتيازات مرتفعة ويتم تسليمها إلى التطبيقات، ننصحك بإيقاف إرسال دوال رد النداء مؤقتًا عندما يدخل التطبيق في حالة التخزين المؤقت، واستئناف الإرسال عند خروجه من هذه الحالة. يمنع ذلك تنفيذ عمليات غير ضرورية في عمليات التطبيقات المخزّنة مؤقتًا.

لتتبُّع وقت دخول التطبيقات إلى حالة التخزين المؤقت أو الخروج منها، استخدِم ActivityManager.addOnUidImportanceListener:

// in ActivityManager or Context
activityManager.addOnUidImportanceListener(
    new UidImportanceListener() { ... },
    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
}

استخدام 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));

إذا كنت تستخدم FROZEN_CALLEE_POLICY_DROP، لن يستدعي النظام callback.onSomeEvent() إلا إذا لم يتم تجميد العملية التي تستضيف دالة الرجوع.

خدمات النظام وتفاعلات التطبيقات

تتفاعل خدمات النظام غالبًا مع العديد من التطبيقات المختلفة باستخدام Binder. بما أنّ التطبيقات يمكن أن تنتقل إلى حالات التخزين المؤقت والتجميد، يجب أن تتخذ خدمات النظام احتياطات خاصة للتعامل مع هذه التفاعلات بسلاسة للمساعدة في الحفاظ على استقرار النظام وأدائه.

يجب أن تتعامل خدمات النظام مع الحالات التي يتم فيها إيقاف عمليات التطبيق لأسباب مختلفة. ويشمل ذلك إيقاف العمل نيابةً عنهم وعدم محاولة مواصلة تسليم عمليات رد الاتصال إلى العمليات المتوقفة. ويُعدّ النظر في تجميد التطبيقات امتدادًا لمسؤولية المراقبة الحالية هذه.

تتبُّع حالات التطبيق من خدمات النظام

يمكن أيضًا للخدمات التابعة لنظام التشغيل، التي تعمل في system_server أو كبرامج خفية أصلية، استخدام واجهات برمجة التطبيقات الموضّحة سابقًا لتتبُّع أهمية عمليات التطبيقات وحالتها المتوقفة:

  • ActivityManager.addOnUidImportanceListener: يمكن لخدمات النظام تسجيل أداة معالجة لتتبُّع التغييرات في أهمية رقم التعريف الفريد. عند تلقّي طلب ربط أو ردّ من تطبيق، يمكن للخدمة استخدام Binder.getCallingUid() للحصول على المعرّف الفريد للمستخدم وربطه بحالة الأهمية التي يتتبّعها المستمع. يتيح ذلك لخدمات النظام معرفة ما إذا كان تطبيق الاتصال في حالة مخزّنة مؤقتًا.

  • IBinder.addFrozenStateChangeCallback: عندما تتلقّى خدمة نظام كائنًا من نوع Binder من تطبيق (على سبيل المثال، كجزء من عملية تسجيل لتلقّي عمليات رد الاتصال)، عليها تسجيل FrozenStateChangeCallback في مثيل IBinder المحدّد. يؤدي ذلك إلى إعلام خدمة النظام مباشرةً عند تجميد عملية التطبيق التي تستضيف هذا الرابط أو إلغاء تجميدها.

اقتراحات لخدمات النظام

ننصح جميع خدمات النظام التي قد تتفاعل مع التطبيقات بتتبُّع حالة التخزين المؤقت والتجميد لعمليات التطبيقات التي تتواصل معها. وقد يؤدي عدم الالتزام بذلك إلى حدوث ما يلي:

  • استهلاك الموارد: يمكن أن يؤدي تنفيذ مهام للتطبيقات المخزَّنة مؤقتًا وغير المرئية للمستخدم إلى إهدار موارد النظام.
  • تعطُّل التطبيق: تؤدي طلبات ربط متزامنة إلى التطبيقات المتوقفة إلى تعطّلها. تؤدي طلبات ربط غير متزامنة إلى التطبيقات المجمّدة إلى حدوث عطل إذا تجاوزت سعة المخزن المؤقت للمعاملات غير المتزامنة.
  • سلوك التطبيق غير المتوقّع: ستتلقّى التطبيقات التي تم إلغاء تجميدها على الفور أي معاملات غير متزامنة مخزّنة مؤقتًا تم إرسالها إليها أثناء تجميدها. يمكن أن تبقى التطبيقات في وضع التجميد لفترة غير محدودة، لذا قد تكون المعاملات المخزّنة مؤقتًا قديمة جدًا.

تستخدم خدمات النظام غالبًا RemoteCallbackList لإدارة عمليات معاودة الاتصال عن بُعد والتعامل تلقائيًا مع العمليات المتوقفة. للتعامل مع التطبيقات المجمدة، يمكنك توسيع نطاق الاستخدام الحالي لـ RemoteCallbackList من خلال تطبيق سياسة المتصل المجمدة كما هو موضح في استخدام RemoteCallbackList.