يمكن تغيير بعض إعدادات الجهاز أثناء تشغيل التطبيق. ويشمل ذلك، على سبيل المثال لا الحصر:
- حجم عرض التطبيق
- اتجاه الشاشة
- حجم الخط ووزنه
- اللغة
- الوضع الداكن مقابل الوضع الفاتح
- مدى توفّر لوحة المفاتيح
تحدث معظم تغييرات الإعدادات هذه بسبب تفاعل المستخدم. على سبيل المثال، يؤدي تدوير الجهاز أو طيّه إلى تغيير مساحة الشاشة المتاحة لتطبيقك، كما أنّ تغيير إعدادات الجهاز، مثل حجم الخط أو اللغة أو المظهر المفضّل، يؤدي إلى تغيير القيم الخاصة بها في العنصر Configuration.
تتطلّب هذه المَعلمات عادةً تغييرات كبيرة بما يكفي في واجهة مستخدم تطبيقك، لذا يتضمّن نظام Android الأساسي آلية مصمّمة خصيصًا للتعامل مع هذه التغييرات.
تُعرف هذه الآلية باسم إعادة إنشاء Activity.
إعادة إنشاء النشاط
يعيد النظام إنشاء Activity عند حدوث تغيير في الإعدادات. ولإجراء ذلك، يستدعي النظام onDestroy() ويزيل مثيل Activity الحالي. بعد ذلك، يتم إنشاء مثيل جديد باستخدام onCreate()، ويتم إعداد مثيل Activity الجديد هذا باستخدام الإعدادات الجديدة والمعدَّلة. يعني هذا أيضًا أنّ النظام يعيد إنشاء واجهة المستخدم باستخدام الإعدادات الجديدة.
يساعد سلوك إعادة الإنشاء تطبيقك على التكيّف مع الإعدادات الجديدة من خلال إعادة تحميل تطبيقك تلقائيًا باستخدام موارد بديلة تتوافق مع إعدادات الجهاز الجديدة.
مثال على الأنشطة الترفيهية
لنفترض أنّ هناك TextView يعرض عنوانًا ثابتًا باستخدام
android:text="@string/title"، كما هو محدّد في ملف XML للتصميم. عند إنشاء طريقة العرض، يتم ضبط النص مرة واحدة فقط استنادًا إلى اللغة الحالية. في حال تغيّر اللغة، يعيد النظام إنشاء النشاط. نتيجةً لذلك، يعيد النظام أيضًا إنشاء العرض ويضبطه على القيمة الصحيحة استنادًا إلى اللغة الجديدة.
تؤدي إعادة الإنشاء أيضًا إلى محو أي حالة تم الاحتفاظ بها كحقول في Activity أو في أي من العناصر Fragment أو View أو غيرها من العناصر المضمّنة. ويرجع ذلك إلى أنّ إعادة إنشاء Activity تؤدي إلى إنشاء نسخة جديدة تمامًا من Activity وواجهة المستخدم. بالإضافة إلى ذلك، لم يعُد Activity القديم مرئيًا أو صالحًا، لذا فإنّ أي مراجع متبقية إليه أو إلى الكائنات التي يتضمّنها أصبحت قديمة. ويمكن أن تتسبّب في حدوث أخطاء وتسريب للذاكرة وتعطُّل.
توقّعات المستخدمين
يتوقّع مستخدم التطبيق الاحتفاظ بالحالة. إذا كان المستخدم يملأ نموذجًا وفتح تطبيقًا آخر في وضع النوافذ المتعددة للرجوع إلى المعلومات، ستكون تجربة المستخدم سيئة إذا عاد إلى نموذج تم محوه أو إلى مكان آخر في التطبيق تمامًا. بصفتك مطوِّرًا، عليك توفير تجربة مستخدم متّسقة من خلال تغييرات الإعدادات وإعادة إنشاء الأنشطة.
للتحقّق مما إذا كان يتم الاحتفاظ بالحالة في تطبيقك، يمكنك تنفيذ إجراءات تؤدي إلى تغييرات في الإعدادات أثناء عمل التطبيق في المقدّمة وأثناء عمله في الخلفية. وتتضمن هذه الإجراءات:
- تدوير الجهاز
- تفعيل وضع النوافذ المتعددة
- تغيير حجم التطبيق في وضع النوافذ المتعددة أو في نافذة حرة الشكل
- طي جهاز قابل للطي مزوّد بشاشات عرض متعددة
- تغيير مظهر النظام، مثل الوضع الداكن مقابل الوضع الفاتح
- تغيير حجم الخط
- تغيير لغة النظام أو التطبيق
- توصيل لوحة مفاتيح خارجية أو قطع الاتصال بها
- ربط قاعدة شحن أو إلغاء ربطها
هناك ثلاث طرق أساسية يمكنك اتّباعها للحفاظ على الحالة ذات الصلة أثناء إعادة إنشاء Activity. يعتمد اختيار أحدهما على نوع الحالة التي تريد الاحتفاظ بها:
- الثبات المحلي للتعامل مع إيقاف العملية نهائيًا للبيانات المعقّدة أو الكبيرة
تشمل مساحة التخزين المحلية الدائمة قواعد البيانات أو
DataStore. - العناصر المحتفظ بها، مثل مثيلات
ViewModelللتعامل مع الحالة المرتبطة بواجهة المستخدم في الذاكرة أثناء استخدام المستخدم للتطبيق بنشاط - حالة المثيل المحفوظة للتعامل مع إيقاف العملية نهائيًا الذي يبدأه النظام والاحتفاظ بالحالة المؤقتة التي تعتمد على بيانات أدخلها المستخدم أو التنقّل
للاطّلاع على تفاصيل حول واجهات برمجة التطبيقات الخاصة بكل من هذه الحالات، ومعرفة الوقت المناسب لاستخدام كل منها، راجِع مقالة حفظ حالات واجهة المستخدم.
حظر إعادة إنشاء النشاط
يمكنك منع إعادة إنشاء النشاط تلقائيًا عند إجراء تغييرات معيّنة في الإعدادات.
يؤدي إعادة إنشاء Activity إلى إعادة إنشاء واجهة المستخدم بأكملها، وأي عناصر مشتقة من Activity. قد تكون لديك أسباب وجيهة لتجنُّب ذلك. على سبيل المثال، قد لا يحتاج تطبيقك إلى تعديل الموارد أثناء تغيير إعدادات معيّنة، أو قد يكون لديك قيود على الأداء. في هذه الحالة، يمكنك الإعلان عن أنّ نشاطك يتعامل مع تغيير الإعدادات بنفسه ومنع النظام من إعادة تشغيل نشاطك.
لإيقاف إعادة إنشاء النشاط عند إجراء تغييرات معيّنة في الإعدادات، أضِف نوع الإعداد إلى android:configChanges في إدخال <activity> في ملف AndroidManifest.xml. تظهر القيم المحتملة في مستندات السمة android:configChanges.
يؤدي رمز البيان التالي إلى إيقاف إعادة إنشاء Activity عند MyActivity عند تغيير اتجاه الشاشة وتوفُّر لوحة المفاتيح:
<activity
android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="@string/app_name">
تؤدي بعض تغييرات الإعدادات دائمًا إلى إعادة تشغيل النشاط. ولا يمكنك إيقافها. على سبيل المثال، لا يمكنك إيقاف تغيير الألوان الديناميكية الذي تم تقديمه في Android 12L (مستوى واجهة برمجة التطبيقات 32).
الاستجابة لتغييرات الإعدادات في نظام العرض
في نظام View، عند حدوث تغيير في الإعدادات أوقفت فيه إعادة إنشاء Activity، يتلقّى النشاط طلبًا إلى Activity.onConfigurationChanged(). تتلقّى أي طرق عرض مرفقة أيضًا طلبًا إلى View.onConfigurationChanged(). بالنسبة إلى تغييرات الإعدادات التي لم تتم إضافتها إلى android:configChanges، يعيد النظام إنشاء النشاط كالمعتاد.
تتلقّى طريقة رد الاتصال onConfigurationChanged() عنصر Configuration يحدّد إعداد الجهاز الجديد. اقرأ الحقول في عنصر Configuration لتحديد الإعداد الجديد. لإجراء التغييرات اللاحقة، عدِّل الموارد التي تستخدمها في واجهتك. عندما يستدعي النظام هذا الإجراء، يتم تعديل عنصر Resources الخاص بالنشاط لعرض الموارد استنادًا إلى الإعداد الجديد. يتيح لك ذلك إعادة ضبط عناصر واجهة المستخدم بدون أن يعيد النظام تشغيل نشاطك.
على سبيل المثال، تتحقّق عملية التنفيذ التالية onConfigurationChanged() مما إذا كانت لوحة المفاتيح متاحة:
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Checks whether a keyboard is available
if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
} else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
}
}
Java
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks whether a keyboard is available
if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
} else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
}
}
إذا لم تكن بحاجة إلى تعديل تطبيقك استنادًا إلى تغييرات الإعدادات هذه، يمكنك بدلاً من ذلك عدم تنفيذ onConfigurationChanged(). في هذه الحالة، سيستمر استخدام جميع الموارد المستخدَمة قبل تغيير الإعدادات، ولن يتم إعادة تشغيل نشاطك. على سبيل المثال، قد لا يريد تطبيق بث تلفزيوني الاستجابة عند توصيل لوحة مفاتيح بلوتوث أو فصلها.
الاحتفاظ بالحالة
عند استخدام هذا الأسلوب، يجب الاحتفاظ بالحالة أثناء دورة حياة النشاط العادية. ويرجع ذلك إلى ما يلي:
- التغييرات التي لا يمكن تجنُّبها: يمكن أن تؤدي تغييرات الإعدادات التي لا يمكنك منعها إلى إعادة تشغيل تطبيقك.
- إيقاف العملية نهائيًا: يجب أن يكون تطبيقك قادرًا على التعامل مع إيقاف العملية نهائيًا الذي يبدأه النظام. إذا خرج المستخدم من تطبيقك وانتقل التطبيق إلى الخلفية، قد يوقف النظام التطبيق.
الاستجابة لتغييرات الإعدادات في Jetpack Compose
تتيح Jetpack Compose لتطبيقك الاستجابة بسهولة أكبر لتغييرات الإعدادات.
ومع ذلك، إذا أوقفت إعادة إنشاء Activity لجميع تغييرات الإعدادات التي يمكن إجراؤها، سيظل تطبيقك ملزمًا بالتعامل مع تغييرات الإعدادات بشكل صحيح.
يتوفّر العنصر Configuration في التسلسل الهرمي لواجهة مستخدم Compose مع LocalConfiguration المحلي للتركيب. وعندما تتغيّر، تتم إعادة إنشاء الدوال المركّبة التي تقرأ من LocalConfiguration.current. لمزيد من المعلومات حول طريقة عمل البيانات المحلية في التركيب، يمكنك الاطّلاع على البيانات ذات النطاق المحلي باستخدام CompositionLocal.
مثال
في المثال التالي، تعرض دالة قابلة للإنشاء تاريخًا بتنسيق محدّد.
تتفاعل الدالة المركّبة مع تغييرات إعدادات اللغة في النظام من خلال استدعاء ConfigurationCompat.getLocales() مع LocalConfiguration.current.
@Composable
fun DateText(year: Int, dayOfYear: Int) {
val dateTimeFormatter = DateTimeFormatter.ofPattern(
"MMM dd",
ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
)
Text(
dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
)
}
لتجنُّب إعادة إنشاء Activity عند تغيير اللغة، يجب أن يوقف Activity الذي يستضيف رمز Compose إمكانية تغيير إعدادات اللغة. لإجراء ذلك، عليك ضبط android:configChanges على locale|layoutDirection.
تغييرات الإعدادات: المفاهيم الرئيسية وأفضل الممارسات
في ما يلي المفاهيم الأساسية التي يجب معرفتها عند إجراء تغييرات في الإعدادات:
- الإعدادات: تحدّد إعدادات الجهاز طريقة عرض واجهة المستخدم للمستخدم، مثل حجم عرض التطبيق أو اللغة أو مظهر النظام.
- تغييرات الإعدادات: تتغيّر الإعدادات من خلال تفاعل المستخدم. على سبيل المثال، قد يغيّر المستخدم إعدادات الجهاز أو طريقة تفاعله معه. لا يمكن منع إجراء تغييرات في الإعدادات.
- إعادة إنشاء
Activity: تؤدي تغييرات الإعدادات إلى إعادة إنشاءActivityتلقائيًا. هذه آلية مدمَجة لإعادة تهيئة حالة التطبيق وفقًا للإعدادات الجديدة. - التدمير
Activity: تؤدي إعادة إنشاءActivityإلى تدمير النظام لنسخةActivityالقديمة وإنشاء نسخة جديدة بدلاً منها. أصبحت النسخة القديمة غير صالحة. وسيؤدي أي مرجع متبقٍ إلى حدوث تسرُّب للذاكرة أو أخطاء أو أعطال. - الحالة: الحالة في مثيل
Activityالقديم غير متوفّرة في مثيلActivityالجديد، لأنّهما مثيلا عنصر مختلفان. الحفاظ على حالة التطبيق والمستخدم كما هو موضّح في حفظ حالات واجهة المستخدم - إيقاف الميزة: يُعدّ إيقاف ميزة إعادة إنشاء النشاط لنوع من تغييرات الإعدادات تحسينًا محتملاً. ويجب أن يتم تحديث تطبيقك بشكل صحيح استجابةً للإعدادات الجديدة.
لتقديم تجربة جيدة للمستخدم، اتّبِع أفضل الممارسات التالية:
- الاستعداد للتغييرات المتكررة في الإعدادات: لا تفترض أنّ التغييرات في الإعدادات نادرة أو لا تحدث أبدًا، بغض النظر عن مستوى واجهة برمجة التطبيقات أو عامل الشكل أو مجموعة أدوات واجهة المستخدم. عندما يجري المستخدم تغييرًا في الإعدادات، يتوقّع أن يتم تحديث التطبيقات وأن تواصل العمل بشكل صحيح مع الإعدادات الجديدة.
- الاحتفاظ بالحالة: لا تفقد حالة المستخدم عند إعادة إنشاء
Activity. احتفِظ بالحالة كما هو موضّح في حفظ حالات واجهة المستخدم. - تجنُّب إيقاف
Activityكحلّ سريع: لا توقِفActivityكحلّ سريع لتجنُّب فقدان الحالة. يتطلّب إيقاف إعادة إنشاء النشاط استيفاء شرط التعامل مع التغيير، وسيظل بإمكانك فقدان الحالة بسبب إعادة إنشاءActivityمن تغييرات الإعدادات الأخرى أو إيقاف العملية نهائيًا أو إغلاق التطبيق. من المستحيل إيقاف إعادة إنشاءActivityبالكامل. احتفِظ بالحالة كما هو موضّح في حفظ حالات واجهة المستخدم. - عدم تجنُّب تغييرات الإعدادات: لا تفرض قيودًا على اتجاه الشاشة أو نسبة العرض إلى الارتفاع أو إمكانية تغيير الحجم لتجنُّب تغييرات الإعدادات وإعادة إنشاء
Activity. ويؤثر ذلك سلبًا في المستخدمين الذين يريدون استخدام تطبيقك بالطريقة التي يفضّلونها.
التعامل مع تغييرات الإعدادات المستندة إلى الحجم
يمكن أن تحدث تغييرات في الإعدادات استنادًا إلى الحجم في أي وقت، ويزداد احتمال حدوثها عندما يتم تشغيل تطبيقك على جهاز بشاشة كبيرة حيث يمكن للمستخدمين تفعيل وضع النوافذ المتعددة. ويتوقّعون أن يعمل تطبيقك بشكل جيد في تلك البيئة.
هناك نوعان عامان من التغييرات في الحجم: مهمة وغير مهمة. التغيير الكبير في الحجم هو التغيير الذي يتم فيه تطبيق مجموعة مختلفة من الموارد البديلة على الإعداد الجديد بسبب اختلاف في حجم الشاشة، مثل العرض أو الارتفاع أو أصغر عرض. تشمل هذه الموارد الموارد التي يحدّدها التطبيق نفسه والموارد من أي من مكتباته.
حصر إعادة إنشاء النشاط عند إجراء تغييرات في الإعدادات استنادًا إلى الحجم
عند إيقاف Activity إعادة الإنشاء للتغييرات في الإعدادات المستندة إلى الحجم، لا يعيد النظام إنشاء Activity. بدلاً من ذلك، سيتلقّى مكالمة على الرقم
Activity.onConfigurationChanged(). تتلقّى أي طرق عرض مرفقة طلبًا إلى
View.onConfigurationChanged().
يتم إيقاف إعادة إنشاء Activity لتغييرات الإعدادات المستندة إلى الحجم عند توفّر android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" في ملف البيان.
السماح بإعادة إنشاء النشاط عند إجراء تغييرات في الإعدادات استنادًا إلى الحجم
في نظام التشغيل Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات) والإصدارات الأحدث، لا تحدث إعادة إنشاء Activity إلا عند حدوث تغييرات كبيرة في الإعدادات المستندة إلى الحجم. عندما لا يعيد النظام إنشاء Activity بسبب عدم توفّر حجم كافٍ، قد يستدعي النظام الدالتَين Activity.onConfigurationChanged() وView.onConfigurationChanged() بدلاً من ذلك.
هناك بعض التحذيرات التي يجب مراعاتها بشأن عمليات معاودة الاتصال Activity وView
عندما لا تتم إعادة إنشاء Activity:
- في الإصدارات من Android 11 (المستوى 30 لواجهة برمجة التطبيقات) إلى Android 13 (المستوى 33 لواجهة برمجة التطبيقات)، لا يتم استدعاء
Activity.onConfigurationChanged(). - هناك مشكلة معروفة قد لا يتم فيها استدعاء
View.onConfigurationChanged()في بعض الحالات على نظام التشغيل Android 12L (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات المبكرة من نظام التشغيل Android 13 (المستوى 33 لواجهة برمجة التطبيقات). لمزيد من المعلومات، يُرجى الاطّلاع على هذه المشكلة العلنية. تمت معالجة هذه المشكلة منذ ذلك الحين في الإصدارات اللاحقة من Android 13 وAndroid 14.
بالنسبة إلى الرمز البرمجي الذي يعتمد على الاستماع إلى التغييرات في الإعدادات المستندة إلى الحجم، ننصحك باستخدام أداة View مع View.onConfigurationChanged() تم إلغاء تعريفها بدلاً من الاعتماد على إعادة إنشاء Activity أو Activity.onConfigurationChanged().