في هذه الصفحة، ستتعرّف على مراحل نشاط عنصر قابل للإنشاء وكيفية تحديد Compose ما إذا كان العنصر القابل للإنشاء بحاجة إلى إعادة إنشاء.
نظرة عامة على مراحل النشاط
كما هو موضّح في مستندات إدارة الحالة، يصف Composition واجهة مستخدم تطبيقك ويتم إنشاؤه من خلال تنفيذ الدوال البرمجية القابلة للإنشاء. Composition هي بنية شجرية للعناصر القابلة للإنشاء التي تصف واجهة المستخدم.
عندما يشغّل Jetpack Compose العناصر القابلة للإنشاء للمرة الأولى، أثناء التركيب الأوّلي، سيتتبّع العناصر القابلة للإنشاء التي تستدعيها لوصف واجهة المستخدم في التركيب. بعد ذلك، عندما تتغير حالة تطبيقك، يجدول Jetpack Compose إعادة إنشاء. تحدث إعادة التركيب عندما يعيد Jetpack Compose تنفيذ العناصر القابلة للإنشاء التي ربما تغيّرت استجابةً لتغييرات الحالة، ثم يعدّل التركيب لعرض أي تغييرات.
لا يمكن إنشاء Composition إلا من خلال إنشاء تركيبة أولية، ولا يمكن تعديلها إلا من خلال إعادة التركيب. الطريقة الوحيدة لتعديل "مقطوعة موسيقية" هي إعادة تأليفها.
الشكل 1. دورة حياة عنصر قابل للإنشاء في Composition يدخل إلى المقطوعة الموسيقية، ويتم إعادة تأليفه 0 مرة أو أكثر، ثم يخرج من المقطوعة الموسيقية.
يتم عادةً إعادة التركيب عند إجراء تغيير على عنصر State<T>
. تتتبّع Compose هذه العمليات وتنفّذ جميع العناصر القابلة للإنشاء في Composition التي تقرأ State<T>
معيّنًا، وأي عناصر قابلة للإنشاء تستدعيها ولا يمكن تخطّيها.
إذا تم استدعاء عنصر قابل للإنشاء عدة مرات، يتم وضع عدة مثيلات في Composition. لكل مكالمة دورة حياة خاصة بها في Composition.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
الشكل 2. تمثيل MyComposable
في "المقطوعة الموسيقية" إذا تم استدعاء عنصر قابل للإنشاء عدة مرات، يتم وضع عدة مثيلات في Composition. يشير العنصر الذي له لون مختلف إلى أنّه نسخة منفصلة.
بنية عنصر قابل للإنشاء في Composition
يتم تحديد مثيل العنصر القابل للإنشاء في Composition من خلال موقع الاستدعاء. يعدّ برنامج التجميع في Compose كل موقع اتصال مختلفًا عن غيره. سيؤدي استدعاء دوال قابلة للإنشاء من مواقع استدعاء متعددة إلى إنشاء مثيلات متعددة للدالة القابلة للإنشاء في Composition.
إذا كان العنصر القابل للإنشاء يستدعي عناصر قابلة للإنشاء مختلفة أثناء إعادة الإنشاء مقارنةً بما استدعاه أثناء الإنشاء السابق، سيتمكّن 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
في Composition عندما تتغيّر الحالة ويحدث إعادة إنشاء. يشير اللون نفسه إلى أنّه لم تتم إعادة تركيبه.
على الرغم من أنّ LoginInput
انتقل من أن يتم استدعاؤه أولاً إلى أن يتم استدعاؤه ثانيًا، سيتم الاحتفاظ بنسخة LoginInput
خلال عمليات إعادة الإنشاء. بالإضافة إلى ذلك،
بما أنّ LoginInput
لا يحتوي على أي مَعلمات تم تغييرها أثناء
إعادة التركيب، سيتخطّى Compose استدعاء LoginInput
.
إضافة معلومات إضافية للمساعدة في عمليات إعادة التكوين الذكية
سيؤدي استدعاء عنصر قابل للإنشاء عدة مرات إلى إضافته إلى Composition عدة مرات أيضًا. عند استدعاء عنصر قابل للإنشاء عدة مرات من موقع الاستدعاء نفسه، لا تتوفّر لدى 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 ترتيب التنفيذ بالإضافة إلى موقع الاستدعاء للحفاظ على تمييز المثيل في Composition. إذا تمت إضافة movie
جديد إلى أسفل القائمة، يمكن أن تعيد Compose استخدام العناصر التي سبق أن تم إنشاؤها في Composition لأنّ موضعها في القائمة لم يتغيّر، وبالتالي، تكون قيمة الإدخال movie
هي نفسها بالنسبة إلى هذه العناصر.
الشكل 4 تمثيل MoviesScreen
في التركيب عند إضافة عنصر جديد إلى أسفل القائمة يمكن إعادة استخدام دوال MovieOverview
البرمجية القابلة للإنشاء في Composition. يشير اللون نفسه في 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
في Composition عند إضافة عنصر جديد إلى القائمة. لا يمكن إعادة استخدام عناصر MovieOverview
القابلة للإنشاء، وستتم إعادة تشغيل جميع التأثيرات الجانبية. يشير اللون المختلف في MovieOverview
إلى أنّه تمت إعادة إنشاء العنصر القابل للإنشاء.
من الناحية المثالية، نريد أن نربط هوية مثيل MovieOverview
بهوية movie
التي يتم تمريرها إليه. إذا أعدنا ترتيب قائمة الأفلام، من المفترض أن نعيد ترتيب العناصر في شجرة Composition بشكل مماثل بدلاً من إعادة إنشاء كل عنصر MovieOverview
قابل للإنشاء باستخدام عنصر فيلم مختلف. توفّر Compose طريقة لتحديد القيم التي تريد استخدامها للتعريف عن جزء معيّن من الشجرة، أي key
القابل للإنشاء، لوقت التشغيل.
من خلال تضمين مجموعة من الرموز البرمجية في استدعاء للدالة القابلة للإنشاء الخاصة بالمفتاح مع تمرير قيمة واحدة أو أكثر، سيتم دمج هذه القيم لاستخدامها في تحديد هذا العنصر في التركيب. لا يشترط أن تكون قيمة 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
في Composition عند إضافة عنصر جديد إلى القائمة. بما أنّ عناصر 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
لمثيلَين دائمًا هي نفسها بالنسبة إلى المثيلَين نفسيهما. - إذا تغيّرت سمة عامة من النوع، سيتم إعلام Composition بذلك.
- جميع أنواع السمات العامة ثابتة أيضًا.
هناك بعض الأنواع الشائعة المهمة التي تندرج ضمن هذا العقد والتي سيتعامل معها برنامج التجميع Compose على أنّها ثابتة، على الرغم من أنّه لم يتم تصنيفها بشكل صريح على أنّها ثابتة باستخدام التعليق التوضيحي @Stable
:
- جميع أنواع القيم الأساسية:
Boolean
وInt
وLong
وFloat
وChar
وما إلى ذلك - آلات وترية
- جميع أنواع الدوال (lambdas)
يمكن لجميع هذه الأنواع اتّباع عقد الثبات لأنّها غير قابلة للتغيير. بما أنّ الأنواع غير القابلة للتغيير لا تتغير أبدًا، لا يلزمها إعلام Composition بالتغيير، لذا يكون من الأسهل بكثير الالتزام بهذا العقد.
أحد الأنواع البارزة الثابتة ولكن القابلة للتغيير هو النوع MutableState
في Compose. إذا تم تخزين قيمة في MutableState
، سيتم اعتبار عنصر الحالة بشكل عام ثابتًا لأنّه سيتم إعلام Compose بأي تغييرات في السمة .value
الخاصة بـ State
.
عندما تكون جميع الأنواع التي تم تمريرها كمَعلمات إلى عنصر قابل للإنشاء مستقرة، تتم مقارنة قيم المَعلمات لتحديد ما إذا كانت متساوية استنادًا إلى موضع العنصر القابل للإنشاء في شجرة واجهة المستخدم. يتم تخطّي إعادة التركيب إذا لم تتغيّر أي من القيم منذ الاستدعاء السابق.
لا تعتبر 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 غير مفعّلة
- الحالة وJetpack Compose
- الآثار الجانبية في Compose
- حفظ حالة واجهة المستخدم في Compose