Back Press Handling في Android: الجوهر والآليات والتطبيق

المؤلف: IT Sectr نُشر: 2026-06-10 وقت القراءة: 9 دق

Back Press Handling هي آلية اعتراض ومعالجة زر الرجوع النظامي في Android، والتي تحدد الإجراء الذي يتم تنفيذه عند الضغط عليه. وفقًا لـ Android Developers (2024)، بدءًا من Android 11، تم استبدال الطريقة التقليدية onBackPressed() بـ OnBackPressedDispatcher. تسمح واجهة API الجديدة للمكونات على أي مستوى في التسلسل الهرمي باعتراض الضغط، وليس فقط Activity. الفرق الرئيسي — دعم استدعاءات متعددة في سلسلة ذات أولويات.

الملامح الرئيسية

  • Back Press Handling — آلية معالجة زر الرجوع النظامي في Android
  • OnBackPressedDispatcher — واجهة API الجديدة التي حلت محل onBackPressed() القديمة
  • الاستدعاءات تُسجل بالأولوية وتُنفذ بترتيب السلسلة
  • Jetpack Compose يستخدم BackHandler لاعتراض الضغط في composable
  • Support Library توفر توافقًا عكسيًا حتى API Level 14

ما هو Back Press Handling

Back Press Handling هي آلية نظام في Android تحدد ما يحدث عندما يضغط المستخدم على زر الرجوع المادي أو البرمجي. اعتمادًا على السياق، يمكن أن يؤدي الضغط إلى: إغلاق الشاشة الحالية والعودة إلى الشاشة السابقة، إخفاء لوحة المفاتيح، إغلاق Drawer أو Bottom Sheet، الخروج من التطبيق إذا كان المستخدم على الشاشة الرئيسية.

تطور سلوك زر الرجوع تطور مع كل إصدار من Android. Android 10 قدم التنقل بالإيماءات، Android 11 قدم OnBackPressedDispatcher كواجهة API قياسية، و Android 13 قدم دعمًا محسنًا للإيماءة التنبؤية للرجوع، حيث يعرض النظام رسمًا متحركًا للانتقال قبل تنفيذ الإجراء فعليًا. تتجه Google باستمرار نحو سلوك Back Press قابل للتوقع ومتسق عبر جميع الأجهزة.

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

تطور Back Press API: من onBackPressed إلى OnBackPressedDispatcher

يعكس تاريخ Back Press API في Android التطور العام للمنصة: من طريقة بسيطة في Activity إلى نظام استدعاءات مرن مع دورة حياة ودعم Compose. دعونا نلقي نظرة على ثلاث مراحل من التطور.

عصر onBackPressed (API Level 1–30)

منذ أول Android API، كان زر الرجوع يُعالج في طريقة onBackPressed لفئة Activity. كان المطور يتجاوز هذه الطريقة ويكتب منطقه الخاص. كانت المشكلة أن Fragment و View لم يكونا قادرين على اعتراض الضغط — كل التحكم كان يمر عبر Activity. أدى هذا إلى Activities منتفخة وسلاسل if-else معقدة لتحديد من يجب أن يعالج الضغط.

ظهور OnBackPressedDispatcher (Activity 1.0.0)

مع Activity 1.0.0 (AndroidX)، قدمت Google OnBackPressedDispatcher. هذا موزع مركزي يقبل الاستدعاءات من أي مكون — Activity و Fragment و Dialog و View مخصصة. تُسجل الاستدعاءات بترتيب (عبر priority) ويمكن إضافتها أو إزالتها ديناميكيًا. يتم استدعاء OnBackPressedDispatcher قبل onBackPressed القديم، مما يسمح باعتراض الضغط قبل أن تعالجه Activity.

الإيماءة التنبؤية للرجوع (Android 13+)

قدم Android 13 الإيماءة التنبؤية للرجوع — رسمًا متحركًا للنظام يوضح أين سيؤدي الضغط على رجوع قبل أن يكمل المستخدم الإيماءة. لدعم هذا الرسم المتحرك، يجب على المطورين استخدام OnBackPressedDispatcher وتحديد ما إذا كان الاستدعاء يدعم الرسم المتحرك للنظام عبر خاصية isEnabled. إذا كان الاستدعاء لا يدعم الرسم المتحرك التنبؤي، يعرض النظام رسمًا متحركًا افتراضيًا قد لا يتوافق مع سياق التطبيق.

APISDK الأدنىدعم Fragmentالرجوع التنبؤي
onBackPressedAPI Level 1عبر Activityلا
OnBackPressedDispatcherActivity 1.0.0مباشرجزئي
OnBackPressedDispatcher + lifecycleActivity 1.3.0واعٍ بدورة الحياةكامل

OnBackPressedDispatcher — البنية وسلسلة الاستدعاءات

OnBackPressedDispatcher هو جوهر واجهة Back Press API الجديدة. يدير سلسلة من الاستدعاءات، مستدعيًا إياها بالترتيب حتى يعالج أولها الحدث. إذا لم يعالج أي استدعاء الضغط، ينفذ الموزع الإجراء الافتراضي — استدعاء finish() لـ Activity أو popBackStack() لمكون Navigation.

تسجيل الاستدعاءات بالأولوية

يُسجل الاستدعاء عبر addCallback مع LifecycleOwner وكائن OnBackPressedCallback. يحتوي الاستدعاء على خاصية isEnabled — إذا تم تعيينها على false، يتم تخطي الاستدعاء. للأولوية، يمكنك تمرير قيمة من 0 (الأدنى) إلى Integer.MAX_VALUE. تستخدم Fragment Activity Result API هذه الآلية للتسجيل التلقائي للاستدعاءات المرتبطة بدورة الحياة.

kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val callback = object : OnBackPressedCallback(enabled = true) {
        override fun handleOnBackPressed() {
            if (isDrawerOpen) { closeDrawer() }
            else { isEnabled = false; onBackPressed() }
        }
    }
    onBackPressedDispatcher.addCallback(this, callback)
}

التنظيف الواعي بدورة الحياة

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

سلسلة الاستدعاء

ترتيب الاستدعاء هو عكس ترتيب الإضافة: آخر استدعاء مضاف يحصل على التحكم أولاً. هذا منطقي لأن عنصر واجهة المستخدم الأكثر تداخلاً (على سبيل المثال، Bottom Sheet داخل Fragment) يجب أن يعالج الضغط قبل Fragment الأصلي. إذا كان الاستدعاء الأعمق لا يعالج الضغط (isEnabled = false)، ينتقل التحكم إلى التالي في السلسلة.

Back Press في Fragments والحوارات

توفر Fragment API تكاملها الخاص مع OnBackPressedDispatcher عبر طريقة requireActivity().onBackPressedDispatcher. بدءًا من Fragment 1.2.0، يمكن لكل Fragment تسجيل استدعائه الخاص، الذي يرتبط تلقائيًا بدورة حياة Fragment ويُزال عند تدميره.

الاستدعاء في Fragment

يتم تسجيل استدعاء في Fragment في onCreate أو onViewCreated أو حتى في View نفسها — المهم هو أن LifecycleOwner (Fragment) نشط. عندما ينتقل Fragment إلى حالة STARTED، يتم تمكين الاستدعاء؛ وعند STOPPED، يتم تعطيله. هذا يضمن أن Fragment المخفي (في ViewPager) لن يعالج ضغط الرجوع.

kotlin
class EditorFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val callback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                showDiscardDialog()
            }
        }
        requireActivity().onBackPressedDispatcher.addCallback(this, callback)
    }
}

Dialog و BottomSheetDialog

تعترض الحوارات و BottomSheets ضغط الرجوع تلقائيًا للإغلاق. إذا كنت بحاجة إلى تنفيذ إجراء إضافي قبل الإغلاق — سجل استدعاء بأولوية أعلى. مهم: إذا قمت بتعيين setCancelable(false) على Dialog، لن يتم تفعيل الاستدعاء — هذا سلوك نظامي.

Back Press Handling في Jetpack Compose

يوفر Jetpack Compose واجهة API تصريحية لمعالجة زر الرجوع عبر دالة BackHandler composable. تقبل BackHandler enabled (حالة منطقية) و onBack — استدعاء يُستدعى عند الضغط. إذا كان enabled = false، يتم تمرير الضغط إلى أسفل السلسلة.

BackHandler في Compose

يسجل BackHandler تلقائيًا OnBackPressedCallback في OnBackPressedDispatcher الخاص بـ Activity الأم. يراعي دورة حياة composable: عند الخروج من التركيب، يُحذف الاستدعاء. يمكن ربط enabled بالحالة — على سبيل المثال، إظهار حوار تأكيد فقط إذا كان النموذج يحتوي على تغييرات غير محفوظة.

kotlin
@Composable
fun EditScreen(hasUnsavedChanges: Boolean) {
    BackHandler(enabled = hasUnsavedChanges) {
        // Show confirmation dialog
    }

    Column {
        TextField(value = ..., onValueChange = ...)
    }
}

الرجوع التنبؤي في Compose

الإيماءة التنبؤية للرجوع Predictive back gesture في Compose مدعومة بدءًا من Compose 1.5.0. يتعامل BackHandler تلقائيًا مع الرسم المتحرك لانتقال النظام إذا كان ممكّنًا على الجهاز. للرسوم المتحركة التنبؤية المخصصة، استخدم معدِّل predictiveBackHandler، الذي يُرجع تقدم الإيماءة من 0 إلى 1.

الأخطاء الشائعة وأفضل الممارسات

يبدو Back Press Handling بسيطًا، ولكن في الممارسة العملية يرتكب المطورون عددًا من الأخطاء المنهجية. دعونا نلقي نظرة على المشكلات الأكثر شيوعًا وحلولها بناءً على توصيات Google وخبرة المجتمع.

خطأ: استدعاء finish() دون التحقق من مكدس التنقل

الاستدعاء المباشر لـ finish() في handleOnBackPressed قد يؤدي إلى خروج غير متوقع من التطبيق إذا كانت هناك شاشات خلفية في مكدس التنقل. تحقق دائمًا من NavController.backStack عبر Navigation Component أو Coordinator قبل إغلاق Activity.

خطأ: تجاهل دورة الحياة عند تسجيل الاستدعاءات

إذا قمت بتسجيل استدعاء بدون LifecycleOwner (باستخدام addCallback القديم بدون المعامل)، سيعيش الاستدعاء إلى الأبد وقد يسبب NullPointerException إذا كانت Activity قد دُمّرت بالفعل. استخدم دائمًا addCallback(this, callback) مع LifecycleOwner.

أفضل ممارسة: مقاطعة السلسلة للنوافذ المشروطة

للنوافذ المشروطة (Bottom Sheet، Dialog)، اضبط دائمًا isEnabled = true فقط عندما تكون النافذة مرئية. استخدم addCallback مع لامدا تتحقق من حالة النافذة. يدير Navigation Component هذا تلقائيًا لـ NavHost.

أفضل ممارسة: معالجة الضغط المزدوج

الضغط المزدوج السريع على رجوع قد يؤدي إلى استدعاء مزدوج لـ finish(). استخدم علمًا أو throttleLast للحماية من الاستدعاءات المتكررة خلال 500 مللي ثانية. يعالج Navigation Component هذه الحالة بشكل مضمن، ولكن في السيناريوهات المخصصة تحتاج إلى تطبيق الحماية يدويًا.

kotlin
private var lastBackPressTime = 0L

override fun handleOnBackPressed() {
    val currentTime = System.currentTimeMillis()
    if (currentTime - lastBackPressTime > 500) {
        lastBackPressTime = currentTime
        navigateBack()
    }
}

الأسئلة الشائعة

لماذا أصبح onBackPressed مهملاً؟

onBackPressed أُهمل في Android 11 لأنه يعمل فقط على مستوى Activity. يسمح OnBackPressedDispatcher لأي مكون (Fragment، Dialog، View) باعتراض الضغط من خلال آلية واحدة مع دعم دورة الحياة.

كيف أميز بين ضغط الرجوع وإيماءة السحب؟

يقوم نظام Android بتحويل إيماءة السحب من الحافة إلى ضغطة رجوع نظامية قبل أن يستقبلها التطبيق. على مستوى OnBackPressedDispatcher، لا يمكنك التمييز بين هذين الحدثين — كلاهما يصل كـ handleOnBackPressed.

هل أحتاج إلى دعم زر الرجوع على الأجهزة ذات الإيماءات؟

نعم، معالجة OnBackPressedDispatcher هي نفسها للأجهزة ذات الأزرار الثلاثة والتنقل بالإيماءات. لا يعتمد رمز الاعتراض على نوع التنقل — النظام نفسه يحول الإيماءة إلى استدعاء للموزع.

كيف أختبر الإيماءة التنبؤية للرجوع على المحاكي؟

فعّل الرجوع التنبؤي في Developer Options لمحاكي Android 13+. استخدم ADB: `adb shell settings put global enable_back_animation 1`. بعد التفعيل، سيعرض الرسم المتحرك للنظام معاينة للانتقال عند الضغط على رجوع.

ماذا أفعل إذا لم يتم استدعاء الاستدعاء؟

تحقق من شرطين: يجب أن يكون LifecycleOwner في حالة STARTED أو RESUMED، ويجب أن يكون isEnabled الخاص بالاستدعاء true. إذا تحقق كلا الشرطين، تأكد من إضافة الاستدعاء إلى OnBackPressedDispatcher الصحيح — استخدم requireActivity().onBackPressedDispatcher في Fragment.

الملخص

  • Back Press Handling — آلية معالجة زر الرجوع النظامي في Android
  • OnBackPressedDispatcher — واجهة API الحديثة التي حلت محل onBackPressed() بدءًا من Android 11
  • الاستدعاءات تُسجل عبر addCallback مع LifecycleOwner وتُنظف تلقائيًا
  • سلسلة الاستدعاء — آخر استدعاء مضاف مع isEnabled=true يعالج الضغط أولاً
  • في Fragment يرتبط الاستدعاء بدورة الحياة ويُعطل عند إخفاء الجزء
  • Jetpack Compose يستخدم BackHandler composable للمعالجة التصريحية
  • الإيماءة التنبؤية للرجوع — رسم متحرك لانتقال النظام متاح من Android 13+

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا