در این صفحه، در مورد چرخه حیات یک composable و اینکه Compose چگونه تصمیم میگیرد که یک composable نیاز به ترکیب مجدد دارد، خواهید آموخت.
مروری بر چرخه حیات
همانطور که در مستندات مدیریت وضعیت ذکر شد، یک Composition رابط کاربری برنامه شما را توصیف می کند و با اجرای composable ها تولید می شود. ترکیب یک ساختار درختی از اجزای سازنده است که رابط کاربری شما را توصیف می کند.
هنگامی که Jetpack Compose برای اولین بار فایلهای composable شما را اجرا میکند، در طول ترکیب اولیه ، مواردی را که برای توصیف رابط کاربری خود در یک Composition فرا میخوانید، پیگیری میکند. سپس، هنگامی که وضعیت برنامه شما تغییر می کند، Jetpack Compose یک ترکیب مجدد را برنامه ریزی می کند. Recomposition زمانی است که Jetpack Compose اجزای تشکیل دهنده را که ممکن است در پاسخ به تغییرات حالت تغییر کرده باشند را مجدداً اجرا می کند و سپس ترکیب را به روز می کند تا هرگونه تغییر را منعکس کند.
یک ترکیب تنها می تواند با یک ترکیب اولیه تولید شود و با ترکیب مجدد به روز شود. تنها راه برای اصلاح یک ترکیب، از طریق ترکیب مجدد است.
شکل 1. چرخه حیات یک ترکیب پذیر در ترکیب. وارد Composition می شود، 0 بار یا بیشتر دوباره ترکیب می شود و از Composition خارج می شود.
ترکیب مجدد معمولاً با تغییر به یک شیء State<T>
آغاز می شود. Compose اینها را دنبال میکند و همه کامپوزیتیها را در Composition اجرا میکند که آن State<T>
میخوانند، و هر composableهایی را که آنها فراخوانی میکنند و نمیتوان از آن گذشت .
اگر یک composable چندین بار فراخوانی شود، چندین نمونه در Composition قرار می گیرد. هر فراخوانی چرخه حیات خاص خود را در Composition دارد.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
شکل 2. نمایش MyComposable
در ترکیب. اگر یک composable چندین بار فراخوانی شود، چندین نمونه در Composition قرار می گیرد. عنصری که رنگ متفاوتی دارد نشان دهنده آن است که یک نمونه جداگانه است.
آناتومی یک ترکیب پذیر در ترکیب
نمونه یک composable در Composition توسط سایت فراخوانی آن مشخص می شود. کامپایلر Compose هر سایت تماس را مجزا در نظر می گیرد. فراخوانی composable ها از چندین سایت تماس، چندین نمونه از composable را در Composition ایجاد می کند.
اگر در طول یک ترکیب مجدد، یک Composable ترکیبهای متفاوتی را نسبت به ترکیب قبلی فراخوانی کرد، 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
composable را فراخوانی می کند و همیشه LoginInput
composable را فراخوانی می کند. هر تماس دارای یک سایت تماس و موقعیت منبع منحصر به فرد است که کامپایلر از آن برای شناسایی منحصر به فرد آن استفاده می کند.
شکل 3. نمایش LoginScreen
در ترکیب زمانی که حالت تغییر می کند و یک ترکیب مجدد رخ می دهد. همان رنگ به این معنی است که دوباره ترکیب نشده است.
حتی اگر LoginInput
از اولین فراخوانی به فراخوانی دوم تبدیل شد، نمونه LoginInput
در بین ترکیبات مجدد حفظ خواهد شد. علاوه بر این، از آنجا که LoginInput
هیچ پارامتری ندارد که در ترکیب مجدد تغییر کرده باشد، فراخوانی LoginInput
توسط Compose نادیده گرفته میشود.
اطلاعات اضافی را برای کمک به ترکیب مجدد هوشمند اضافه کنید
چندین بار فراخوانی یک Composable آن را نیز چندین بار به Composition اضافه می کند. هنگام فراخوانی چندین بار از یک سایت تماس یکسان، Compose هیچ اطلاعاتی برای شناسایی منحصر به فرد هر تماس با آن composable ندارد، بنابراین برای متمایز نگه داشتن نمونهها از دستور اجرا علاوه بر سایت تماس استفاده میشود. این رفتار گاهی تنها چیزی است که لازم است، اما در برخی موارد می تواند باعث رفتار ناخواسته شود.
@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 میتواند از نمونههایی که قبلاً در ترکیب قرار دارند، دوباره استفاده کند، زیرا مکان آنها در لیست تغییر نکرده است و بنابراین، ورودی 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
در ترکیب زمانی که یک عنصر جدید به لیست اضافه می شود. ترکیبکنندههای MovieOverview
را نمیتوان مجدداً استفاده کرد و همه عوارض جانبی دوباره شروع میشوند. رنگ متفاوت در MovieOverview
به این معنی است که قابلیت ترکیب دوباره ترکیب شده است.
در حالت ایدهآل، ما میخواهیم هویت نمونه MovieOverview
را به هویت movie
که به آن منتقل میشود مرتبط بدانیم. اگر فهرست فیلمها را دوباره ترتیب دهیم، در حالت ایدهآل، بهطور مشابه به جای اینکه هر MovieOverview
را با یک نمونه فیلم متفاوت ترکیب کنیم، نمونههای موجود در درخت Composition را دوباره ترتیب میدهیم. 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
در ترکیب زمانی که یک عنصر جدید به لیست اضافه می شود. از آنجایی که اجزای سازنده MovieOverview
دارای کلیدهای منحصربهفرد هستند، Compose تشخیص میدهد که کدام نمونههای MovieOverview
تغییر نکردهاند و میتواند دوباره از آنها استفاده کند. عوارض جانبی آنها ادامه خواهد داشت.
برخی از composable ها دارای پشتیبانی داخلی برای composable key
هستند. به عنوان مثال، LazyColumn
تعیین یک key
سفارشی در items
DSL را می پذیرد.
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
اگر ورودیها تغییر نکرده باشند، رد میشوند
در طول ترکیب مجدد، در صورتی که ورودی های آنها نسبت به ترکیب قبلی تغییر نکرده باشد، می توان اجرای برخی از توابع قابل ترکیب واجد شرایط را به طور کامل نادیده گرفت.
یک تابع قابل ترکیب برای پرش واجد شرایط است مگر اینکه :
- تابع دارای نوع بازگشتی غیر
Unit
است - تابع با
@NonRestartableComposable
یا@NonSkippableComposable
حاشیه نویسی شده است - یک پارامتر مورد نیاز از نوع ناپایدار است
یک حالت کامپایلر آزمایشی، Strong Skipping وجود دارد که آخرین نیاز را کاهش می دهد.
برای اینکه یک نوع پایدار در نظر گرفته شود باید با قرارداد زیر مطابقت داشته باشد:
- نتیجه
equals
برای دو نمونه برای همیشه برای همان دو نمونه یکسان خواهد بود. - اگر یک ویژگی عمومی از نوع تغییر کند، Composition اطلاع رسانی خواهد شد.
- همه انواع اموال عمومی نیز پایدار هستند.
برخی از انواع رایج مهم که در این قرارداد قرار میگیرند وجود دارند که کامپایلر Compose آنها را بهعنوان پایدار در نظر میگیرد، حتی اگر با استفاده از حاشیهنویسی @Stable
بهصراحت بهعنوان پایدار علامتگذاری نشدهاند:
- همه انواع مقادیر اولیه:
Boolean
،Int
،Long
،Float
،Char
و غیره. - رشته ها
- همه انواع تابع (لامبدا)
همه این اقسام به دلیل غیرقابل تغییر بودن قادر به پیروی از عقد ثابت هستند. از آنجایی که انواع تغییرناپذیر هرگز تغییر نمی کنند، آنها هرگز نباید Composition را از تغییر مطلع کنند، بنابراین پیروی از این قرارداد بسیار ساده تر است.
یک نوع قابل توجه که پایدار است اما قابل تغییر است ، نوع MutableState
Compose است. اگر مقداری در یک MutableState
نگهداری شود، شی state به طور کلی پایدار در نظر گرفته می شود زیرا Compose از هرگونه تغییر در ویژگی .value
State
مطلع می شود.
وقتی همه انواع ارسال شده به عنوان پارامتر به یک composable پایدار هستند، مقادیر پارامتر برای برابری بر اساس موقعیت قابل ترکیب در درخت 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 تمام پیادهسازیهای خود را پایدار میکند.
برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- State و Jetpack Compose
- عوارض جانبی در Compose
- حالت رابط کاربری را در Compose ذخیره کنید