در این صفحه، با چرخه حیات عنصر ترکیبی و نحوه تصمیمگیری Compose درباره نیاز عنصر ترکیبی به ترکیب مجدد آشنا میشوید.
نمای کلی چرخه حیات
همانطور که در مستندات مدیریت وضعیت ذکر شده است، «ترکیب» رابط کاربری برنامهتان را توصیف میکند و با اجرای عناصر ترکیبی تولید میشود. «ترکیب» ساختار درختی از عناصر ترکیبی است که رابط کاربری شما را توصیف میکند.
وقتی Jetpack Compose برای اولینبار عناصر ترکیبی شما را اجرا میکند، درطول ترکیب اولیه، عناصری ترکیبی را که برای توصیف میانای کاربرتان در «ترکیب» فراخوانی میکنید پیگیری میکند. سپس، وقتی وضعیت برنامه تغییر میکند، Jetpack Compose ترکیب مجدد را زمانبندی میکند. بازترکیب زمانی است که Jetpack Compose ترکیبشوندههایی را که ممکن است در پاسخ به تغییرات حالت تغییر کرده باشند دوباره اجرا میکند، و سپس «ترکیب» را بهروزرسانی میکند تا هرگونه تغییر را منعکس کند.
«قطعه موسیقی» فقط میتواند ازطریق قطعه موسیقی اولیه تولید شود و ازطریق قطعه موسیقی مجدد بهروز شود. تنها راه تغییر دادن «قطعه موسیقی» ازطریق «قطعه موسیقی مجدد» است.
معمولاً تغییر در شیء State<T> باعث بازترکیب میشود. Compose
اینها را ردیابی میکند و همه عناصر ترکیبی را در «ترکیب» که آن State<T> خاص را میخوانند اجرا میکند، و همچنین همه عناصر ترکیبی که آنها را فرا میخوانند و نمیتوانند رد شوند.
اگر یک عنصر ترکیبی چندین بار فراخوانی شود، چندین نمونه در «ترکیب» قرار میگیرد. هر تماس در «ترکیب» چرخه عمر خودش را دارد.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
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 را فراخوانی میکند. هر
تماس دارای سایت تماس و موقعیت منبع منحصربهفردی است که کامپایلر از آن برای
شناسایی منحصربهفرد آن استفاده میکند.
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) } } }
در مثال بالا، «نوشتن» علاوهبر سایت تماس از ترتیب اجرا نیز استفاده میکند تا نمونه را در «ترکیب» متمایز نگه دارد. اگر movie جدیدی به پایین فهرست اضافه شود، «نگارش» میتواند از نمونههای موجود در «نگارش» استفاده مجدد کند زیرا مکان آنها در فهرست تغییر نکرده است و بنابراین، ورودی movie برای آن نمونهها یکسان است.
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) /* ... */ } }
MoviesScreen در «ترکیب» وقتی عنصر جدیدی به فهرست اضافه میشود. از MovieOverview عنصر ترکیبی نمیتوان دوباره استفاده کرد و
همه جلوههای جانبی بازراهاندازی خواهند شد. رنگ متفاوت در MovieOverview یعنی
ترکیبشونده دوباره ترکیب شده است.در حالت ایدهآل، میخواهیم هویت نمونه MovieOverview را بهعنوان
پیوندخورده به هویت movie که به آن منتقل میشود درنظر بگیریم. اگر فهرست فیلمها را تغییر دهیم، در حالت ایدهآل، نمونههای موجود در درخت «ترکیب» را نیز بهطور مشابه تغییر میدهیم، بهجای اینکه هر MovieOverview قابلساخت را با نمونه فیلم متفاوتی بازسازی کنیم. «ترکیب» روشی برای شما فراهم میکند تا به زمان اجرا بگویید
از چه مقادیری میخواهید برای شناسایی بخش معینی از درخت استفاده کنید: عنصر
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) } } } }
با موارد بالا، حتی اگر عناصر فهرست تغییر کند، «نگارش» تماسهای فردی با MovieOverview را تشخیص میدهد و میتواند از آنها مجدداً استفاده کند.
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 مطلع خواهد شد.
وقتی همه انواع منتقلشده بهعنوان پارامتر به یک عنصر ترکیبی پایدار باشند، مقادیر پارامتر براساس موقعیت عنصر ترکیبی در درخت واسط کاربر برای برابری مقایسه میشوند. اگر همه مقادیر از زمان تماس قبلی بدون تغییر مانده باشند، از ترکیب مجدد صرفنظر میشود.
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
حاشیهنویسی، به «ترکیببندی» میگویید که این نوع پایدار است و به «ترکیببندی» اجازه میدهد
ترکیببندیهای هوشمند را ترجیح دهد. این همچنین به این معنی است که اگر از رابط بهعنوان نوع پارامتر استفاده شود، «نوشتن» همه پیادهسازیهای خود را پایدار درنظر میگیرد.
توصیهشده برای شما
- توجه: نوشتار پیوند وقتی جاوا اسکریپت خاموش است نمایش داده میشود
- حالت و Jetpack Compose
- عوارض جانبی در Compose
- ذخیره وضعیت میانای کاربر در Compose