ويمكن أن تتغير بعض إعدادات الأجهزة أثناء تشغيل التطبيق. وهذه تتضمن، على سبيل المثال لا الحصر:
- حجم عرض التطبيق
- اتجاه الشاشة
- حجم الخط وسُمكه
- اللغة
- الوضع الداكن مقابل الوضع الفاتح
- توفُّر لوحة المفاتيح
تحدث معظم تغييرات الضبط هذه بسبب بعض تفاعلات المستخدم. على سبيل المثال، يؤدي تدوير الجهاز أو طيه إلى تغيير مقدار مساحة الشاشة
المتاحة لتطبيقك. وبالمثل، يؤدي تغيير إعدادات الجهاز، مثل حجم الخط أو اللغة أو المظهر المفضَّل إلى تغيير قيمها في الكائن
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
. للحصول على معلومات حول طريقة عمل مقطوعات موسيقية محلّية، يمكنك الاطّلاع على البيانات ذات النطاق المحلي باستخدام SurfaceLocal.
مثال
في المثال التالي، يعرض عنصر قابل للإنشاء تاريخًا بتنسيق معيّن.
يتفاعل العنصر القابل للإنشاء مع التغييرات في إعدادات لغة النظام من خلال طلب الرمز
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
التي تستضيف رمز الإنشاء إيقاف تغييرات إعدادات اللغة. لإجراء ذلك، يمكنك ضبط android:configChanges
على locale|layoutDirection
.
تغييرات الإعدادات: المفاهيم الأساسية وأفضل الممارسات
هذه هي المفاهيم الأساسية التي تحتاج إلى معرفتها عند العمل على تغييرات التهيئة:
- الإعدادات: تحدِّد إعدادات الجهاز طريقة عرض واجهة المستخدم للمستخدم، مثل حجم عرض التطبيق أو اللغة أو مظهر النظام.
- تغييرات الإعدادات: تتغير عمليات الضبط من خلال تفاعل المستخدم. على سبيل المثال، يمكن للمستخدم تغيير إعدادات الجهاز أو كيفية تفاعله فعليًا مع الجهاز. وليس هناك أي طريقة لمنع تغييرات الضبط.
- إعادة إنشاء
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()
.