إصلاح مشاكل الثبات

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

.

تفعيل التخطّي القوي

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

يمكنك الاطّلاع على التخطّي القوي للحصول على مزيد من المعلومات.

جعل الفئة غير قابلة للتغيير

يمكنك أيضًا محاولة جعل فئة غير مستقرة غير قابلة للتغيير تمامًا.

  • غير قابل للتغيير: يشير إلى نوع لا يمكن فيه أبدًا تغيير قيمة أي سمات بعد إنشاء مثيل من هذا النوع، وتكون جميع الطرق شفّافة من حيث المرجع.
    • تأكَّد من أنّ جميع سمات الفئة هي val بدلاً من var، ومن أنواع غير قابلة للتغيير.
    • الأنواع الأساسية، مثل String, Int وFloat، تكون غير قابلة للتغيير دائمًا.
    • إذا كان ذلك مستحيلاً، يجب استخدام حالة الإنشاء لأي خصائص قابلة للتغيير.
  • ثابت: يشير إلى نوع قابل للتغيير. لا يُدرك وقت التشغيل في 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، يصنِّف مجمع ComposeSnack على أنّها فئة ثابتة.

صفوف بتعليقات توضيحية في المجموعات

يمكنك استخدام دالة قابلة للإنشاء تتضمّن مَعلمة من النوع List<Snack>:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  …
  unstable snacks: List<Snack>
  …
)

حتى إذا أضفت تعليقًا توضيحيًا على Snack باستخدام @Immutable، سيظلّ مُجمِّع 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
)

الحل

بعد اتّباع أي من هذَين الأسلوبَين، يصبح الآن المحول البرمجي لإنشاء علامة 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
)

أثناء إعادة التركيب، يمكن الآن لميزة "إنشاء" تخطّي HighlightedSnacks في حال لم يتم تغيير أي من الإدخالات.

ملف إعدادات الثبات

بدءًا من Compose Compiler 1.5.5، يمكن توفير ملف إعداد للفئات المخصصة للثابت في وقت التجميع. يتيح ذلك اعتبار الفئات التي لا تتحكم فيها، مثل فئات المكتبة العادية مثل 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.

إذا كانت طبقة البيانات في وحدة منفصلة عن طبقة واجهة المستخدم، وهي النهج المُقترَح، قد تواجهك مشكلة.

الحل

لحل هذه المشكلة، يمكنك اتّباع أحد الأساليب التالية:

  1. أضِف الفئات إلى ملف إعداد أداة التجميع.
  2. فعِّل مُجمِّع Compose في وحدات طبقة البيانات، أو ضَع علامة على صفوفك باستخدام @Stable أو @Immutable حيثما كان ذلك مناسبًا.
    • ويشمل ذلك إضافة تبعية Compose إلى طبقة البيانات. ومع ذلك، إنّه مجرد عنصر تابع لوقت تشغيل Compose وليس لCompose-UI.
  3. ضمن وحدة واجهة المستخدم، لفّ فئات طبقة البيانات في فئات ملف ملتفٍ خاصة بواجهة المستخدم.

تحدث المشكلة نفسها أيضًا عند استخدام مكتبات خارجية إذا لم تستخدم compiler (مُجمِّع) Compose.

لا يجب أن تكون كل العناصر القابلة للتجميع قابلة للتخطّي.

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

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

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

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