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

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

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

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

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

جعل الصف غير قابل للتغيير

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

  • غير قابل للتغيير: يشير إلى نوع لا يمكن أن تتغير فيه قيمة أي خصائص أبدًا بعد إنشاء مثيل من هذا النوع، وجميع الطرق شفافة مرجعية.
    • تأكَّد من أنّ جميع سمات الفئة هي val وليس var، وأنّها من أنواع غير قابلة للتغيير.
    • تعتبر الأنواع الأساسية مثل String, Int وFloat غير قابلة للتغيير دائمًا.
    • إذا كان ذلك مستحيلاً، فيجب عليك استخدام حالة Compose لأي خصائص قابلة للتغيير.
  • ثابت: يشير إلى نوع قابل للتغيير. لا يتم اعتبار وقت تشغيل ComposeAllowed ما إذا كان أي من الخصائص العامة أو سلوك الأسلوب للنوع سينتج نتائج مختلفة عن استدعاء سابق.

المجموعات غير القابلة للتغيير

تعد المجموعات أحد الأسباب الشائعة وراء اعتبار 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، يضع برنامج التجميع البرمجي علامة على الفئة Snack على أنّها ثابتة.

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

ننصحك بإنشاء عنصر قابل للإنشاء يتضمّن معلَمة من النوع 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
)

الحل

بعد اتّباع أي من هذَين الأسلوبَين، أصبحت أداة التحويل البرمجي لـ ComposeAllowed على أنّها HighlightedSnacks Composable على أنّها 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 إذا لم يتم تغيير أي من إدخالاتها.

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

بدءًا من إصدار Compose Compiler 1.5.5، يمكن توفير ملف إعداد للفئات التي يجب اعتبارها مستقرًا في وقت التجميع. يتيح لك ذلك تصنيف الصفوف التي لا تتحكّم فيها، مثل فئات المكتبة العادية مثل LocalDateTime، على أنّها ثابتة.

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

// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider kotlin collections stable
kotlin.collections.*
// 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<*,_>

لتفعيل هذه الميزة، مرِّر مسار ملف الإعداد إلى خيارات برنامج التحويل البرمجي في Compose.

رائع

kotlinOptions {
    freeCompilerArgs += [
            "-P",
            "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
                    project.absolutePath + "/compose_compiler_config.conf"
    ]
}

Kotlin

kotlinOptions {
  freeCompilerArgs += listOf(
      "-P",
      "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
      "${project.absolutePath}/compose_compiler_config.conf"
  )
}

نظرًا لتشغيل برنامج التجميع البرمجي Compose على كل وحدة في مشروعك بشكل منفصل، يمكنك توفير تهيئات مختلفة لوحدات مختلفة إذا لزم الأمر. وبدلاً من ذلك، يمكنك استخدام تهيئة واحدة على مستوى الجذر في مشروعك وتمرير هذا المسار إلى كل وحدة.

وحدات متعددة

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

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

الحل

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

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

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

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

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

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

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

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