في هذه الصفحة، ستتعرّف على مراحل نشاط الدالة المركّبة وكيفية تحديد Compose ما إذا كانت الدالة المركّبة بحاجة إلى إعادة تركيب.
نظرة عامة على مراحل النشاط
كما هو موضّح في مستند إدارة الحالة، يصف التركيب واجهة المستخدم في تطبيقك ويتم إنشاؤه من خلال تشغيل الدوال المركّبة. "التركيب" هو بنية شجرية للدوال المركّبة التي تصف واجهة المستخدم.
عندما يشغّل Jetpack Compose الدوال المركّبة للمرة الأولى، أثناء الإنشاء الأولي، سيتتبّع الدوال المركّبة التي تستدعيها لوصف واجهة المستخدم في عملية إنشاء. بعد ذلك، عندما تتغيّر حالة تطبيقك، يجدول Jetpack Compose إعادة تركيب. إعادة التركيب هي عندما يعيد Jetpack Compose تنفيذ الدوال المركّبة التي قد تكون تغيّرت استجابةً لتغييرات الحالة، ثم يحدّث التركيب ليعكس أي تغييرات.
لا يمكن إنشاء "تركيبة" إلا من خلال تركيبة أولية، ولا يمكن تعديلها إلا من خلال إعادة التركيب. الطريقة الوحيدة لتعديل "إنشاء" هي من خلال إعادة الإنشاء.
يتم عادةً بدء إعادة الإنشاء من خلال تغيير في عنصر
State<T>. يتتبّع Compose
هذه التغييرات ويشغّل جميع الدوال المركّبة في التركيب التي تقرأ
State<T> المحدّد، وأي دوال مركّبة تستدعيها ولا يمكن
تخطّيها.
إذا تم استدعاء دالة مركّبة عدة مرات، يتم وضع نُسخ متعددة في التركيب. لكل مكالمة مراحل نشاط خاصة بها في Composition.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
MyComposable" في "المقطوعة الموسيقية" إذا تم استدعاء دالة مركّبة عدة مرات، يتم وضع نُسخ متعددة في التركيب. ويشير اختلاف لون العنصر إلى أنّه نسخة منفصلة.بنية الدالة المركّبة في Composition
يتم تحديد مثيل دالة مركّبة في "التركيب" من خلال موقع الاستدعاء. يعتبر برنامج تجميع 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. لكل استدعاء موقع استدعاء وموضع مصدر فريد، وسيستخدمهما برنامج التجميع لتحديده بشكل فريد.
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 هو نفسه لهذه النُسخ.
MoviesScreen في Composition عند إضافة عنصر جديد إلى أسفل القائمة يمكن إعادة استخدام الدوال المركّبة 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) /* ... */ } }
MoviesScreen في Composition عند إضافة عنصر جديد إلى القائمة لا يمكن إعادة استخدام الدوال المركّبة MovieOverview، وستتم إعادة تشغيل جميع الآثار الجانبية. يشير اللون المختلف في MovieOverview إلى أنّه تمت إعادة تركيب الدالة المركّبة.من الناحية المثالية، نريد أن نفكر في هوية مثيل MovieOverview على أنها
مرتبطة بهوية movie التي تم تمريرها إليه. إذا أعدنا ترتيب قائمة الأفلام، من الأفضل أن نعيد ترتيب النماذج في شجرة التركيب بدلاً من إعادة تركيب كل دالة مركّبة MovieOverview باستخدام نموذج فيلم مختلف. توفّر أداة Compose طريقة لإعلام وقت التشغيل
بالقيم التي تريد استخدامها لتحديد جزء معيّن من الشجرة: الدالة المركّبة key.
من خلال تضمين مجموعة من الرموز البرمجية مع استدعاء الدالة المركّبة "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 ويمكنه إعادة استخدامها.
MoviesScreen في Composition عند إضافة عنصر جديد إلى القائمة بما أنّ الدوال المركّبة MovieOverview لها مفاتيح فريدة، يتعرّف Compose على نُسخ MovieOverview التي لم تتغيّر، ويمكنه إعادة استخدامها، وستستمر آثارها الجانبية في التنفيذ.تتضمّن بعض الدوال المركّبة دعمًا مضمّنًا للدالة المركّبة key. على سبيل المثال، يقبل LazyColumn تحديد key مخصّص في لغة DSL لـ items.
@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