تشخيص مشاكل الثبات

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

أداة فحص التنسيق

تتيح لك "أداة فحص التنسيق" في استوديو Android معرفة الدوال المركّبة التي تتم إعادة تكوينها في تطبيقك، وتعرض عدد المرات التي أعادت فيها Compose تكوين مكوّن أو تخطّته.

عدد عمليات إعادة الإنشاء والتخطّي في "أداة فحص التنسيق"

تقارير برنامج تجميع Compose

يمكن لبرنامج تجميع Compose إخراج نتائج استنتاج الثبات للفحص. باستخدام هذه النتيجة، يمكنك تحديد الدوال المركّبة التي يمكن تخطّيها والتي لا يمكن تخطّيها. تلخّص الفقرات الفرعية التالية كيفية استخدام هذه التقارير، ولكن لمزيد من المعلومات التفصيلية، يمكنك الاطّلاع على المستندات الفنية.

الإعداد

لا يتم تفعيل تقارير برنامج تجميع Compose تلقائيًا. ويمكنك تفعيلها باستخدام علامة برنامج تجميع. تختلف عملية الإعداد الدقيقة حسب مشروعك، ولكن بالنسبة إلى المشاريع التي تستخدم المكوّن الإضافي Compose compiler gradle، يمكنك إضافة ما يلي في ملف build.gradle لكل وحدة.

  android { ... }

  composeCompiler {
    reportsDestination = layout.buildDirectory.dir("compose_compiler")
    metricsDestination = layout.buildDirectory.dir("compose_compiler")
  }

سيتم الآن إنشاء تقارير برنامج تجميع Compose عند إنشاء مشروعك.

مثال على الناتج

يُخرج reportsDestination ثلاثة ملفات. في ما يلي أمثلة على النتائج من JetSnack.

  • <modulename>-classes.txt: تقرير عن ثبات الفئات في هذه الوحدة. نموذج
  • ‫<modulename>-composables.txt: تقرير حول مدى قابلية إعادة تشغيل الدوال المركّبة وتخطّيها في الوحدة. نموذج
  • <modulename>-composables.csv:هو إصدار CSV من تقرير الدوال المركّبة الذي يمكنك استيراده إلى جدول بيانات أو معالجته باستخدام نص برمجي. عيّنة

تقرير الدوال المركّبة

يعرض ملف composables.txt تفاصيل كل دالة مركّبة للوحدة المحدّدة، بما في ذلك ثبات مَعلماتها، وما إذا كانت قابلة لإعادة التشغيل أو التخطّي. في ما يلي مثال افتراضي من JetSnack:

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun SnackCollection(
  stable snackCollection: SnackCollection
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
  stable index: Int = @static 0
  stable highlight: Boolean = @static true
)

في هذا المثال، يتم وضع علامة stable على كل مَعلمة من مَعلمات SnackCollection، لذا يقارن Compose كل مَعلمة باستخدام المساواة الهيكلية (Object.equals()). على الرغم من أنّ مَعلمات stable مثالية للقيم الأساسية وفئات البيانات الصغيرة والمسطحة التي تتضمّن عمليات تحقّق .equals() منخفضة التكلفة، فإنّ فرض النماذج التي تحتوي على مجموعات (مثل SnackCollection التي تلتف حول List<Snack>) لتكون stable يؤدي إلى استدعاء Compose لـ .equals() في كل عنصر في كل إعادة تركيب. عند تفعيل وضع التخطّي القوي، يُفضَّل الاحتفاظ بالنماذج التي تحتوي على مجموعات unstable ليقارنها Compose باستخدام مساواة المثيلات (===) بدلاً من ذلك.

يمكنك بدلاً من ذلك الاطّلاع على مثال آخر تم إنشاؤه مع إيقاف وضع التخطّي القوي:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  unstable snacks: List<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

عند إيقاف وضع التخطّي القوي (قبل إصدار Kotlin 2.0.20)، لا يمكن تخطّي الدالة المركّبة HighlightedSnacks، ولا تتخطّاها أداة Compose أبدًا أثناء إعادة التركيب، حتى إذا لم تتغيّر أي من مَعلماتها، وذلك بسبب المَعلمة unstable snacks.

عند تفعيل وضع التخطّي القوي (الوضع التلقائي في Kotlin 2.0.20 والإصدارات الأحدث)، يتم وضع علامة skippable على HighlightedSnacks، ويقارن Compose مَعلمة unstable snacks باستخدام مساواة المثيل (===). ونتيجةً لذلك، يتخطّى Compose HighlightedSnacks إذا تم تمرير مرجع List نفسه، ولا يعيد إنشاءه إلا إذا تم تمرير مثيل List تم تخصيصه حديثًا.

تقرير الصفوف

يحتوي الملف classes.txt على تقرير مشابه عن الفئات في الوحدة المحددة. المقتطف التالي هو الناتج للفئة Snack:

unstable class Snack {
  stable val id: Long
  stable val name: String
  stable val imageUrl: String
  stable val price: Long
  stable val tagline: String
  unstable val tags: Set<String>
  <runtime stability> = Unstable
}

للمرجع، إليك تعريف Snack:

data class Snack(
    val id: Long,
    val name: String,
    val imageUrl: String,
    val price: Long,
    val tagline: String = "",
    val tags: Set<String> = emptySet()
)

وضع برنامج تجميع Compose علامة على Snack على أنّه غير مستقر. ويرجع ذلك إلى أنّ نوع المَعلمة "tags" هو "Set<String>". هذا نوع غير قابل للتغيير، لأنّه ليس MutableSet. ومع ذلك، فإن فئات المجموعات القياسية مثل Set وList وMap هي في النهاية واجهات. وعلى هذا النحو، قد يظل التنفيذ الأساسي قابلاً للتغيير.

على سبيل المثال، يمكنك كتابة val set: Set<String> = mutableSetOf("foo"). المتغيّر ثابت ونوعه المعلَن غير قابل للتغيير، ولكن تنفيذه لا يزال قابلاً للتغيير. لا يمكن لبرنامج تجميع Compose التأكّد من ثبات هذه الفئة لأنّه يرى النوع المُعلَن عنه فقط. وبالتالي، يتم وضع علامة على tags على أنّه غير مستقر.