في هذه الصفحة، ستتعرّف على دورة حياة العنصر القابل للتجميع و كيف تقرّر أداة Compose ما إذا كان العنصر القابل للتجميع بحاجة إلى إعادة التركيب.
نظرة عامة على مراحل النشاط
كما هو موضّح في مستندات إدارة الحالة، تصف التركيبة واجهة مستخدم تطبيقك ويتم إنشاؤها من خلال تشغيل العناصر القابلة للتجميع. التركيبة هي بنية شجرة للعناصر القابلة للتجميع التي تصف واجهة المستخدم.
عندما يشغِّل Jetpack Compose العناصر القابلة للتجميع لأول مرة، سيتتبّع أثناء التركيب الأولي العناصر القابلة للتجميع التي تستدعيها لوصف واجهة المستخدم في تركيبة. عندما تتغير حالة تطبيقك بعد ذلك، يحدِّد Jetpack Compose عملية إعادة إنشاء. تتم إعادة التركيب عندما يُعيد Jetpack Compose تنفيذ العناصر القابلة للتجميع التي قد تغيّرت استجابةً لتغييرات الحالة، ثم يعدّل التركيب ليعكس أي تغييرات.
ولا يمكن إنتاج مقطوعة موسيقية إلا من خلال مقطوعة موسيقية أولية ويتم تعديلها من خلال إعادة الإنشاء. إنّ الطريقة الوحيدة لتعديل المقطوعة الموسيقية هي من خلال إعادة تركيبها.
الشكل 1: دورة حياة عنصر قابل للتجميع في التركيب يدخل المقطع إلى المقطوعة الموسيقية، ويتم إعادة إنشائه 0 مرة أو أكثر، ويغادر المقطوعة الموسيقية.
يتم عادةً بدء عملية إعادة التركيب نتيجةً لتغيير في كائن
State<T>
. يمكنك إنشاء مقاطع صوتية ثم إعادة تشغيل كل العناصر القابلة للإنشاء في المقطوعة الموسيقية التي تتضمّن 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
جديدة
إلى أسفل القائمة، يمكن لميزة Compose إعادة استخدام المثيلات المضمّنة في المقطوعة الموسيقية
لأنّ موقعها في القائمة لم يتغيّر، وبالتالي، يكون الإدخال 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) } } } }
باستخدام ما سبق، حتى إذا تغيّرت العناصر في القائمة، سيتعرّف Compose على المكالمات الفردية إلى MovieOverview
ويمكنه إعادة استخدامها.
الشكل 6: تمثيل MoviesScreen
في التركيب عند إضافة عنصر
جديد إلى القائمة بما أنّ العناصر القابلة للتجميع من MovieOverview
لها مفاتيح
فريدة، يتعرّف Compose على نُسخ MovieOverview
التي لم تتغيّر، وي
يمكنه إعادة استخدامها، وسيستمر تنفيذ تأثيراتها الجانبية.
تتوفّر لبعض العناصر القابلة للتجميع ميزة مدمجة تتيح استخدام العنصر القابل للتجميع key
. على سبيل المثال،
يقبل LazyColumn
تحديد key
مخصّص في items
DSL.
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
التخطّي إذا لم تتغيّر الإدخالات
أثناء إعادة التركيب، يمكن تخطي التنفيذ بالكامل لبعض الدوال المؤهلة القابلة للإنشاء إذا لم تتغير إدخالاتها عن التركيبة السابقة.
تكون الدالة المركّبة مؤهّلة للتخطّي إلا إذا:
- نوع الإرجاع للدالة ليس
Unit
- تتم إضافة تعليقات توضيحية إلى الدالة باستخدام
@NonRestartableComposable
أو@NonSkippableComposable
. - المَعلمة المطلوبة من نوع غير ثابت
هناك وضع تجريبي للمحول البرمجي، التخطي القوي، يخفف من المتطلبات الأخيرة.
لكي يُعتبر النوع ثابتًا، يجب أن يمتثل للعقد التالي:
- ستكون نتيجة
equals
في المثيلَين إلى الأبد هي نفسها بالنسبة إلى المثيلَين نفسيهما. - وإذا تغيّرت ملكية عامة من هذا النوع، يتم إرسال إشعار إلى المقطوعة الموسيقية.
- جميع أنواع المواقع الإلكترونية العامة ثابتة أيضًا.
هناك بعض الأنواع الشائعة المهمة التي تندرج ضمن هذا العقد والتي سيتعامل معها المحول البرمجي في Compose على أنّها ثابتة، على الرغم من عدم الإشارة صراحةً إلى أنّها ثابتة باستخدام تعليق @Stable
التوضيحي:
- جميع أنواع القيم الأساسية:
Boolean
وInt
وLong
وFloat
وChar
وما إلى ذلك - الأوتار
- جميع أنواع الدوالّ (دالّات لامبادا)
يمكن لجميع هذه الأنواع اتّباع اتّفاقية الثبات لأنّها غير قابلة للتغيير. ونظرًا لأن الأنواع غير القابلة للتغيير لا تتغير أبدًا، فلن تضطر أبدًا إلى إبلاغ تكوين التغيير، لذا يصبح اتباع هذا العقد أسهل بكثير.
من بين الأنواع البارزة التي تكون ثابتة ولكن يمكن تغييرها، نوع 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
- الآثار الجانبية في Compose
- حفظ حالة واجهة المستخدم في Compose