في هذه الصفحة، ستتعرّف على دورة حياة العنصر القابل للتجميع و كيف تقرّر أداة Compose ما إذا كان العنصر القابل للتجميع بحاجة إلى إعادة التركيب.
نظرة عامة على مراحل النشاط
كما هو موضّح في مستندات إدارة الحالة، تصف التركيبة واجهة مستخدم تطبيقك ويتم إنشاؤها من خلال تشغيل العناصر القابلة للتجميع. التركيبة هي بنية شجرة للعناصر القابلة للتجميع التي تصف واجهة المستخدم.
عندما يشغِّل Jetpack Compose العناصر القابلة للتجميع لأول مرة، سيتتبّع أثناء التركيب الأولي العناصر القابلة للتجميع التي تستدعيها لوصف واجهة المستخدم في تركيبة. بعد ذلك، عندما تتغيّر حالة تطبيقك، يحدّد Jetpack Compose موعدًا لإعادة الإنشاء. تتم إعادة التركيب عندما يُعيد Jetpack Compose تنفيذ العناصر القابلة للتجميع التي قد تغيّرت استجابةً لتغييرات الحالة، ثم يعدّل التركيب ليعكس أي تغييرات.
لا يمكن إنشاء تركيبة إلا من خلال تركيبة أولية وتعديلها من خلال إعادة التركيب. لا يمكن تعديل تركيبة إلا من خلال إعادة التركيب.
الشكل 1: دورة حياة عنصر قابل للتجميع في التركيب يدخل المقطع إلى المقطوعة الموسيقية، ويتم إعادة إنشائه 0 مرة أو أكثر، ويغادر المقطوعة الموسيقية.
يتم عادةً بدء عملية إعادة التركيب نتيجةً لتغيير في كائن
State<T>
. تتتبّع Compose
هذه العناصر وتعمل على تشغيل جميع العناصر القابلة للتجميع في التركيب التي تقرأ
State<T>
المحدّد، وأي عناصر قابلة للتجميع تطلبها ولا يمكن
تخطّيها.
في حال استدعاء عنصر قابل للتجميع عدة مرات، يتم وضع نُسخ متعددة منه في التركيب. ولكلّ مكالمة دورة حياة خاصة بها في التركيب.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
الشكل 2: تمثيل MyComposable
في التكوين في حال استدعاء ملف
قابل للتركيب عدة مرات، يتم وضع نُسخ متعددة منه في
التركيب. يشير اللون المختلف للعنصر إلى أنّه
مثيل منفصل.
بنية العنصر القابل للتجميع في "التركيب"
يتم تحديد مثيل العنصر القابل للتجميع في التكوين من خلال موقع الاستدعاء. يعتبر مُجمِّع Compose كل موقع اتصال مختلفًا. سيؤدي استدعاء العناصر القابلة للتجميع من مواقع اتصال متعددة إلى إنشاء مثيلات متعددة للعنصر القابل للتجميع في التركيب.
إذا كانت العناصر القابلة للتجميع تستدعي عناصر قابلة للتجميع مختلفة أثناء إعادة التركيب مقارنةً بتلك التي كانت تستدعيها أثناء التركيب السابق، سيحدِّد تطبيق Compose العناصر القابلة للتجميع التي تمّت استدعاؤها أو عدم استدعاؤها، وبالنسبة إلى العناصر القابلة للتجميع التي تمّت استدعاؤها في كلتا التركيبتَين، سيتجنّب تطبيق Compose إعادة تركيبها إذا لم تتغيّر مدخلاتها.
إنّ الحفاظ على الهوية أمرٌ بالغ الأهمية لربط التأثيرات الجانبية بالعناصر القابلة للتجميع، كي يتمكّنوا من إكمالها بنجاح بدلاً من إعادة التشغيل عند كل إعادة تركيب.
راجِع المثال التالي:
@Composable fun LoginScreen(showError: Boolean) { if (showError) { LoginError() } LoginInput() // This call site affects where LoginInput is placed in Composition } @Composable fun LoginInput() { /* ... */ } @Composable fun LoginError() { /* ... */ }
في مقتطف الرمز أعلاه، سيستدعي LoginScreen
بشكل مشروط العنصر القابل للتجميع
LoginError
وسيستدعي دائمًا العنصر القابل للتجميع LoginInput
. ولكل دعوة موقع دعوة وموقع مصدر فريدَين، وسيستخدمهما المُجمِّع لتحديدها بشكل فريد.
الشكل 3: تمثيل LoginScreen
في التركيب عند تغيُّر الحالة
وإعادة التركيب يشير اللون نفسه إلى أنّه لم تتم إعادة تركيبه.
على الرغم من أنّه تم استدعاء LoginInput
أولاً ثم ثانيًا،
سيتم الاحتفاظ بمثيل LoginInput
في جميع عمليات إعادة التركيب. بالإضافة إلى ذلك،
بما أنّ LoginInput
لا يحتوي على أيّ مَعلمات تغيّرت في
إعادة التركيب، سيتخطّى Compose طلب الاتصالLoginInput
.
إضافة معلومات إضافية للمساعدة في عمليات إعادة التركيب الذكية
سيؤدي استدعاء عنصر قابل للتركيب عدة مرات إلى إضافته إلى المقطوعة الموسيقية عدة مرات أيضًا. عند استدعاء عنصر قابل للتركيب عدة مرات من موقع الاستدعاء نفسه، لا يحتوي Compose على أي معلومات لتحديد كل استدعاء لهذا العنصر القابل للتركيب بشكل فريد، لذلك يتم استخدام ترتيب التنفيذ بالإضافة إلى موقع الاستدعاء للحفاظ على تمايز الحالات. في بعض الأحيان، يكون هذا السلوك هو كل ما تحتاجه، ولكن في بعض الحالات، يمكن أن يؤدي إلى سلوك غير مرغوب فيه.
@Composable fun MoviesScreen(movies: List<Movie>) { Column { for (movie in movies) { // MovieOverview composables are placed in Composition given its // index position in the for loop MovieOverview(movie) } } }
في المثال أعلاه، يستخدم Compose ترتيب التنفيذ بالإضافة إلى موقع الويب المُشار إليه في الطلب للحفاظ على تمييز المثيل في التركيب. في حال إضافة movie
جديد
إلى أسفل القائمة، يمكن لميزة "الإنشاء" إعادة استخدام النُسخ التي سبق أن تم تضمينها في
التركيبة لأنّ موقعها في القائمة لم يتغيّر، وبالتالي، فإنّ إدخالmovie
هو نفسه بالنسبة إلى هذه النُسخ.
الشكل 4: تمثيل MoviesScreen
في التركيب عند إضافة عنصر
جديد إلى أسفل القائمة يمكن إعادة استخدام MovieOverview
عنصر قابل للتجميع في
التركيب. يعني اللون نفسه في MovieOverview
أنّه لم تتم إعادة تركيب العنصر القابل للتجميع.
ومع ذلك، إذا تغيّرت قائمة movies
عن طريق الإضافة إلى الجزء العلوي أو
الوسط من القائمة، أو إزالة العناصر أو إعادة ترتيبها، سيؤدي ذلك إلى إعادة التركيب
في جميع طلبات MovieOverview
التي تغيّر موضع مَعلمة الإدخال فيها في
القائمة. وهذا مهم للغاية إذا كان MovieOverview
، على سبيل المثال، يُجلب
صورة فيلم باستخدام تأثير جانبي. إذا حدثت إعادة التركيب أثناء تنفيذ التأثير، سيتم إلغاؤه وإعادة تنفيذه.
@Composable fun MovieOverview(movie: Movie) { Column { // Side effect explained later in the docs. If MovieOverview // recomposes, while fetching the image is in progress, // it is cancelled and restarted. val image = loadNetworkImage(movie.url) MovieHeader(image) /* ... */ } }
الشكل 5: تمثيل MoviesScreen
في التركيب عند إضافة عنصر
جديد إلى القائمة لا يمكن إعادة استخدام العناصر القابلة للتجميع في MovieOverview
، وسيتم إعادة تشغيل
جميع التأثيرات الجانبية. يشير اللون المختلف في MovieOverview
إلى أنّه تمت إعادة تركيب
العنصر القابل للتجميع.
من الناحية المثالية، نريد اعتبار هوية مثيل MovieOverview
مرتبطة
بهوية movie
التي يتم تمريرها إليه. إذا أعدنا ترتيب
قائمة الأفلام، من الأفضل أن نعيد ترتيب النُسخ في ملف
شجرة التركيب بدلاً من إعادة إنشاء كل MovieOverview
قابلة للتركيب باستخدام
نسخة فيلم مختلفة. توفّر Compose طريقة لإعلام وقت التشغيل
بالقيم التي تريد استخدامها لتحديد جزء معيّن من الشجرة: key
composable.
من خلال لفّ مجموعة من الرموز البرمجية مع طلب إلى المفتاح القابل للتركيب مع قيمة واحدة أو أكثر
يتم تمريرها، سيتم دمج هذه القيم لاستخدامها لتحديد
هذا المثال في التركيبة. لا يجب أن تكون قيمة key
فريدة على مستوى التطبيق، بل يجب أن تكون فريدة فقط بين عمليات استدعاء
العناصر القابلة للتجميع في موقع الاستدعاء. في هذا المثال، يجب أن يتضمّن كل movie
key
فريدًا بين movies
، ولا بأس إذا كان يتشارك هذاkey
مع
بعض العناصر القابلة للتجميع الأخرى في مكان آخر من التطبيق.
@Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { // Unique ID for this movie MovieOverview(movie) } } } }
باستخدام الخطوات أعلاه، حتى إذا تغيّرت العناصر في القائمة، يتعرّف تطبيق "الإنشاء" على
طلبات فردية إلى MovieOverview
ويمكنه إعادة استخدامها.
الشكل 6: تمثيل MoviesScreen
في التركيب عند إضافة عنصر
جديد إلى القائمة بما أنّ العناصر القابلة للتجميع من MovieOverview
لها مفاتيح
فريدة، يتعرّف Compose على نُسخ MovieOverview
التي لم تتغيّر، وي
يمكنه إعادة استخدامها، وسيستمر تنفيذ تأثيراتها الجانبية.
تتوفّر لبعض العناصر القابلة للتجميع ميزة مدمجة تتيح استخدام العنصر القابل للتجميع key
. على سبيل المثال،
يقبل LazyColumn
تحديد key
مخصّص في لغة برمجة items
.
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
التخطّي إذا لم تتغيّر الإدخالات
أثناء إعادة التركيب، يمكن تخطّي تنفيذ بعض الدوالّ المؤهّلة القابلة للتركيب بالكامل إذا لم تتغيّر مدخلاتها عن عملية التركيب السابقة.
تكون الدالة المركّبة مؤهّلة للتخطّي إلا إذا:
- نوع الإرجاع للدالة ليس
Unit
- تمّت إضافة تعليق توضيحي للدالة باستخدام
@NonRestartableComposable
أو@NonSkippableComposable
- نوع المَعلمة المطلوبة غير ثابت
هناك وضع مترجم تجريبي، وهو التخطّي القوي، الذي يُخفّف من المتطلّب الأخير.
لكي يُعتبر النوع ثابتًا، يجب أن يمتثل للعقد التالي:
- ستكون نتيجة
equals
في المثيلَين إلى الأبد هي نفسها بالنسبة إلى المثيلَين نفسيهما. - إذا تغيّر موقع علني من النوع، سيتم إشعار "التركيب".
- جميع أنواع المواقع العامة مستقرة أيضًا.
هناك بعض الأنواع الشائعة والمهمة التي تندرج ضمن هذا العقد وسيتعامل معها compilador Compose على أنّها مستقرة، حتى لو لم يتم وضع علامة عليها صراحةً على أنّها مستقرة باستخدام التعليق التوضيحي @Stable
:
- جميع أنواع القيم الأساسية:
Boolean
وInt
وLong
وFloat
وChar
وما إلى ذلك - الأوتار
- جميع أنواع الدوالّ (دالّات لامبادا)
يمكن لجميع هذه الأنواع اتّباع اتّفاقية الثبات لأنّها غير قابلة للتغيير. وبما أنّ الأنواع الثابتة لا تتغيّر أبدًا، لن يحتاجوا إلى إبلاغ Composition بالتغيير، لذا من الأسهل بكثير اتّباع هذا العقد.
من بين الأنواع البارزة التي تكون ثابتة ولكن يمكن تغييرها، نوع MutableState
في Compose. إذا تم الاحتفاظ بقيمة في MutableState
، يتم بشكل عام
اعتبار عنصر الحالة مستقرًا لأنّ Compose سيتم إعلامه بأي تغييرات تطرأ على .value
State
.
عندما تكون جميع الأنواع التي تم تمريرها كمَعلمات إلى عنصر قابل للتركيب ثابتة، تتم مقارنة قيم المَعلمة للتأكّد من تطابقها استنادًا إلى موضع العنصر القابل للتركيب في شجرة UI. يتم تخطّي إعادة التركيب إذا لم تتغيّر جميع القيم منذ المكالمة السابقة.
لا تعتبر أداة Compose النوع ثابتًا إلا إذا تمكّنت من إثبات ذلك. على سبيل المثال، يتم التعامل مع واجهة بشكل عام على أنّها غير ثابتة، والأنواع التي تحتوي على خصائص عامة قابلة للتغيير والتي يمكن أن يكون تنفيذها ثابتًا ليست ثابتة أيضًا.
إذا لم تتمكّن أداة Compose من استنتاج أنّ نوعًا معيّنًا ثابت، ولكنّك تريد إجبار
Compose على التعامل معه على أنّه ثابت، يمكنك وضع علامة عليه باستخدام التعليق التوضيحي
@Stable
.
// Marking the type as stable to favor skipping and smart recompositions. @Stable interface UiState<T : Result<T>> { val value: T? val exception: Throwable? val hasError: Boolean get() = exception != null }
في مقتطف الرمز أعلاه، بما أنّ UiState
واجهة، يمكن أن تصنّف أداة Compose
هذا النوع على أنّه غير ثابت في العادة. من خلال إضافة التعليق التوضيحي @Stable
، تُعلم Compose بأنّ هذا النوع ثابت، ما يسمح لتطبيق Compose بتفضيل
عمليات إعادة الإنشاء الذكية. ويعني ذلك أيضًا أنّ Compose سيتعامل مع جميع
عمليات التنفيذ على أنّها مستقرة إذا تم استخدام الواجهة كنوع المَعلمة.
أفلام مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما تكون لغة JavaScript غير مفعّلة.
- State وJetpack Compose
- الآثار الجانبية في ميزة "الإنشاء"
- حفظ حالة واجهة المستخدم في ميزة "الإنشاء"