عند مواجهة صف غير مستقر يؤدي إلى مشاكل في الأداء ، عليك جعله مستقرًا. يحدّد هذا المستند عدة أساليب يمكنك استخدامها لإجراء ذلك.
.تفعيل ميزة "التخطّي القوي"
عليك أولاً محاولة تفعيل وضع "التخطّي القوي". يتيح وضع "التخطّي القوي" تخطّي العناصر المركّبة التي تتضمّن مَعلمات غير مستقرة، وهو أسهل طريقة لإصلاح مشاكل الأداء الناتجة عن عدم الاستقرار.
لمزيد من المعلومات، يُرجى الاطّلاع على مقالة التخطّي القوي.
جعل الصف غير قابل للتغيير
يمكنك أيضًا محاولة جعل صف غير مستقر غير قابل للتغيير تمامًا.
- غير قابل للتغيير: يشير إلى نوع لا يمكن تغيير قيمة أي من خصائصه بعد إنشاء مثيل من هذا النوع، وتكون جميع الطرق شفافة من الناحية المرجعية.
- تأكَّد من أنّ جميع خصائص الصف هي
valبدلاً منvar، ومن أنواع غير قابلة للتغيير. - تكون الأنواع الأساسية، مثل
String, IntوFloat، غير قابلة للتغيير دائمًا. - إذا كان ذلك غير ممكن، عليك استخدام حالة Compose لأي خصائص قابلة للتغيير.
- تأكَّد من أنّ جميع خصائص الصف هي
- مستقر: يشير إلى نوع قابل للتغيير. لا يصبح وقت تشغيل Compose على علم بما إذا كان أي من الخصائص العامة للنوع أو سلوك الطريقة سيؤدي إلى نتائج مختلفة عن استدعاء سابق، ومتى سيحدث ذلك.
المجموعات غير القابلة للتغيير
من الأسباب الشائعة التي تجعل Compose يعتبر صفًا غير مستقر هي المجموعات. كما هو موضّح
في صفحة تشخيص مشاكل الاستقرار، لا يمكن لمترجم Compose
التأكّد تمامًا من أنّ المجموعات، مثل List, Map وSet، غير قابلة للتغيير فعلاً
، وبالتالي يضع علامة عليها بأنّها غير مستقرة.
لحلّ هذه المشكلة، يمكنك استخدام مجموعات غير قابلة للتغيير. يتضمّن مترجم Compose دعمًا لـ مجموعات Kotlinx غير القابلة للتغيير. تم تصميم هذه المجموعات لتكون غير قابلة للتغيير، ويتعامل معها مترجم Compose على هذا الأساس. لا تزال هذه المكتبة في مرحلة الإصدار الأولي، لذا توقَّع إجراء تغييرات محتملة على واجهة برمجة التطبيقات الخاصة بها.
لنأخذ مرة أخرى هذا الصف غير المستقر من دليل تشخيص مشاكل الاستقرار:
unstable class Snack {
…
unstable val tags: Set<String>
…
}
يمكنك جعل tags مستقرًا باستخدام مجموعة غير قابلة للتغيير. في الصف، غيِّر
نوع tags إلى ImmutableSet<String>:
data class Snack{
…
val tags: ImmutableSet<String> = persistentSetOf()
…
}
بعد إجراء ذلك، تصبح جميع مَعلمات الصف غير قابلة للتغيير، ويضع مترجم Compose علامة على الصف بأنّه مستقر.
إضافة تعليق توضيحي باستخدام Stable أو Immutable
من الطرق المحتملة لحلّ مشاكل الاستقرار إضافة تعليق توضيحي باستخدام @Stable أو @Immutable للصفوف غير المستقرة.
تؤدي إضافة تعليق توضيحي إلى صف إلى تجاوز ما قد يستنتجه المترجم عن صفك بخلاف ذلك
يشبه ذلك عامل التشغيل
!! في Kotlin. عليك توخي الحذر الشديد بشأن كيفية استخدام هذه التعليقات التوضيحية. قد يؤدي تجاوز سلوك المترجم إلى حدوث أخطاء غير متوقّعة، مثل عدم إعادة تركيب العنصر المركّب عندما تتوقّع ذلك.
إذا كان من الممكن جعل صفك مستقرًا بدون إضافة تعليق توضيحي، عليك السعي لتحقيق الاستقرار بهذه الطريقة.
يوضّح المقتطف التالي مثالاً بسيطًا على صف بيانات تم وضع علامة عليه بأنّه غير قابل للتغيير:
@Immutable
data class Snack(
…
)
سواء استخدمت التعليق التوضيحي @Immutable أو @Stable، يضع مترجم Compose علامة على الصف Snack بأنّه مستقر.
الصفوف التي تم وضع علامة عليها في المجموعات
لنأخذ دالة مركّبة تتضمّن مَعلمة من النوع List<Snack>
restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
…
unstable snacks: List<Snack>
…
)
حتى إذا أضفت تعليقًا توضيحيًا باستخدام @Immutable إلى Snack، سيظل مترجم Compose يضع علامة على
المَعلمة snacks في HighlightedSnacks بأنّها غير مستقرة.
تواجه المَعلمات المشكلة نفسها التي تواجهها الصفوف عندما يتعلق الأمر بأنواع المجموعات،
يضع مترجم Compose دائمًا علامة على مَعلمة من النوع List بأنّها غير مستقرة، حتى
عندما تكون مجموعة من الأنواع المستقرة.
لا يمكنك وضع علامة على مَعلمة فردية بأنّها مستقرة، ولا يمكنك إضافة تعليق توضيحي إلى دالة مركّبة ليكون قابلاً للتخطّي دائمًا. هناك عدة طرق للمتابعة.
هناك عدة طرق لتجنُّب مشكلة المجموعات غير المستقرة. توضّح الأقسام الفرعية التالية هذه الأساليب المختلفة.
ملف الإعداد
إذا كنت على استعداد للالتزام بعقد الاستقرار في قاعدة الرموز البرمجية، يمكنك الموافقة على اعتبار مجموعات Kotlin مستقرة من خلال إضافة kotlin.collections.* إلى ملف إعدادات الاستقرار.
مجموعة غير قابلة للتغيير
لضمان عدم إمكانية تغيير البيانات في وقت التجميع، يمكنك استخدام مجموعة kotlinx غير قابلة للتغيير بدلاً من List.
@Composable
private fun HighlightedSnacks(
…
snacks: ImmutableList<Snack>,
…
)
Wrapper
إذا لم تتمكّن من استخدام مجموعة غير قابلة للتغيير، يمكنك إنشاء مجموعة خاصة بك. لإجراء ذلك، عليك تضمين List في صف مستقر تم وضع علامة عليه. من المرجّح أنّ أفضل خيار لذلك هو برنامج تضمين عام، وذلك حسب متطلباتك.
@Immutable
data class SnackCollection(
val snacks: List<Snack>
)
يمكنك بعد ذلك استخدام هذا النوع للمَعلمة في العنصر المركّب.
@Composable
private fun HighlightedSnacks(
index: Int,
snacks: SnackCollection,
onSnackClick: (Long) -> Unit,
modifier: Modifier = Modifier
)
الحل
بعد اتّباع أي من هذين الأسلوبَين، يضع مترجم Compose الآن علامة على العنصر المركّب
HighlightedSnacks بأنّه skippable وrestartable.
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
stable index: Int
stable snacks: ImmutableList<Snack>
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
)
أثناء إعادة التركيب، يمكن أن يتخطّى Compose الآن HighlightedSnacks إذا لم تتغيّر أي من بياناته المدخلة.
ملف إعدادات الاستقرار
بدءًا من الإصدار 1.5.5 من مترجم Compose، يمكن توفير ملف إعدادات للصفوف التي يجب اعتبارها مستقرة في وقت التجميع. يتيح ذلك اعتبار الصفوف التي لا تتحكّم بها، مثل صفوف المكتبة العادية مثل LocalDateTime، مستقرة.
ملف الإعدادات هو ملف نص عادي يتضمّن صفًا واحدًا لكل سطر. يتم دعم التعليقات وأحرف البدل المفردة والمزدوجة.
في ما يلي مثال على الإعدادات:
// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider my datalayer stable
com.datalayer.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>
لتفعيل هذه الميزة، مرِّر مسار ملف الإعدادات إلى كتلة خيارات
composeCompiler في إعدادات مكوّن Gradle الإضافي لمترجم Compose.
composeCompiler {
stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}
بما أنّ مترجم Compose يتم تشغيله على كل وحدة في مشروعك بشكل منفصل، يمكنك تقديم إعدادات مختلفة لوحدات مختلفة إذا لزم الأمر. بدلاً من ذلك، يمكنك استخدام إعداد واحد على مستوى الجذر في مشروعك وتمرير هذا المسار إلى كل وحدة.
وحدات متعددة
هناك مشكلة شائعة أخرى تتعلق ببنية الوحدات المتعددة. لا يمكن لمترجم Compose استنتاج ما إذا كان الصف مستقرًا إلا إذا تم وضع علامة صريحة على جميع الأنواع غير الأساسية التي يشير إليها بأنّها مستقرة أو إذا كانت في وحدة تم إنشاؤها أيضًا باستخدام مترجم Compose.
إذا كانت طبقة البيانات في وحدة منفصلة عن طبقة واجهة المستخدم، وهو الأسلوب المقترَح، قد تواجه هذه المشكلة.
الحل
لحلّ هذه المشكلة، يمكنك اتّباع أحد الأسلوبَين التاليَين:
- إضافة الصفوف إلى ملف إعدادات المترجم
- تفعيل مترجم Compose على وحدات طبقة البيانات، أو وضع علامة
@Stableأو@Immutableعلى صفوفك حسب الاقتضاء- يتضمّن ذلك إضافة تبعية Compose إلى طبقة البيانات. ومع ذلك، فإنّها مجرد تبعية لوقت تشغيل Compose وليس لـ
Compose-UI.
- يتضمّن ذلك إضافة تبعية Compose إلى طبقة البيانات. ومع ذلك، فإنّها مجرد تبعية لوقت تشغيل Compose وليس لـ
- في وحدة واجهة المستخدم، يمكنك تضمين صفوف طبقة البيانات في صفوف برامج تضمين خاصة بواجهة المستخدم.
تحدث المشكلة نفسها أيضًا عند استخدام مكتبات خارجية إذا لم تكن تستخدم مترجم Compose.
لا ينبغي أن تكون كل دالة مركّبة قابلة للتخطّي
عند العمل على إصلاح مشاكل الاستقرار، يجب ألا تحاول جعل كل دالة مركّبة قابلة للتخطّي. قد يؤدي محاولة إجراء ذلك إلى تحسين مبكر يؤدي إلى حدوث مشاكل أكثر من المشاكل التي يتم إصلاحها.
هناك العديد من الحالات التي لا يكون فيها العنصر المركّب قابلاً للتخطّي، ولا يحقق أي فائدة حقيقية، وقد يؤدي إلى رموز يصعب صيانتها. على سبيل المثال:
- عنصر مركّب لا تتم إعادة تركيبه بشكل متكرر أو لا تتم إعادة تركيبه على الإطلاق
- عنصر مركّب لا يستدعي بحد ذاته سوى عناصر مركّبة قابلة للتخطّي
- دالة مركّبة تتضمّن عددًا كبيرًا من المَعلمات مع عمليات تنفيذ باهظة الثمن للدالة equals في هذه الحالة، قد تفوق تكلفة التحقّق مما إذا كانت أي مَعلمة قد تغيّرت تكلفة إعادة التركيب الرخيصة.
عندما يكون العنصر المركّب قابلاً للتخطّي، فإنّه يضيف حملاً بسيطًا قد لا يكون مفيدًا. يمكنك حتى إضافة تعليق توضيحي إلى العنصر المركّب ليكون غير قابل لإعادة التشغيل في الحالات التي تحدّد فيها أنّ كونه قابلاً لإعادة التشغيل يمثّل حملاً أكبر من قيمته.