در این صفحه، با چرخه حیات یک composable و نحوه تصمیمگیری Compose درباره نیاز یک composable به recomposition آشنا خواهید شد.
نمای کلی چرخه حیات
همانطور که در سند مدیریت حالت ذکر شده است، «ترکیببندی» میانای کاربر برنامه شما را توصیف میکند و با اجرای composableها تولید میشود. «ترکیببندی» ساختاری درختی از composable است که واسط کاربر شما را توصیف میکند.
وقتی Jetpack Compose برای اولینبار درطول ترکیببندی اولیه، composableهای شما را اجرا میکند، composableهایی را که برای توصیف میانای کاربر در «ترکیببندی» فراخوانی میکنید ردیابی میکند. سپس، وقتی وضعیت برنامهتان تغییر میکند، Jetpack Compose یک بازترکیب را زمانبندی میکند. بازترکیب زمانی است که Jetpack Compose ترکیبشدنیهایی را که ممکن است در پاسخ به تغییرات حالت تغییر کرده باشند دوباره اجرا میکند، و سپس «ترکیببندی» را بهروز میکند تا هرگونه تغییری را منعکس کند.
«ترکیببندی» فقط میتواند با ترکیببندی اولیه تولید شود و با ترکیببندی مجدد بهروز شود. تنها راه اصلاح «ترکیببندی» ازطریق ترکیببندی مجدد است.
بازترکیب معمولاً با تغییر در شیء
State<T> راهاندازی میشود. Compose
این موارد را ردیابی میکند و همه composable-های موجود در Composition را که آن
State<T> خاص را میخوانند و هر composable-ای را که آنها فراخوانی میکنند و نمیتوان از آن
رد شد اجرا میکند.
اگر یک composable چندین بار فراخوانی شود، چندین نمونه در Composition قرار میگیرد. هر فراخوانی چرخه حیات خودش را در «ترکیببندی» دارد.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
MyComposable در «ترکیببندی». اگر یک
composable چندین بار فراخوانی شود، چندین نمونه در
Composition قرار میگیرد. اگر رنگ عنصری متفاوت باشد، نشاندهنده این است که آن عنصر نمونهای جداگانه است.کالبدشکافی یک ترکیبشدنی در «ترکیببندی»
نمونه یک composable در Composition با call site آن شناسایی میشود. کامپایلر 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 صرفنظر خواهد کرد.
برای کمک به بازترکیبهای هوشمند، اطلاعات اضافی اضافه کنید
چندبار فراخوانی کردن یک ترکیبشدنی آن را چندبار به «ترکیببندی» اضافه میکند. وقتی یک composable چندین بار از یک سایت فراخوانی فراخوانی میشود، 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 علاوهبر سایت فراخوانی از ترتیب اجرا استفاده میکند تا نمونه را در «ترکیببندی» متمایز نگه دارد. اگر movie جدیدی به پایین فهرست اضافه شود، Compose میتواند از نمونههایی که قبلاً در Composition وجود دارند مجدداً استفاده کند، زیرا مکان آنها در فهرست تغییر نکرده است و بنابراین، ورودی 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 را با نمونه فیلم متفاوتی بازترکیب کنیم. 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 را تشخیص میدهد و میتواند از آنها مجدداً استفاده کند.
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حاشیهنویسی شده است - پارامتر الزامی از نوع ناپایدار است
یک حالت کامپایلر بهنام Strong Skipping وجود دارد که شرط آخر را آسانتر میکند.
برای اینکه نوعی پایدار درنظر گرفته شود، باید با قرارداد زیر مطابقت داشته باشد:
- نتیجه
equalsبرای دو نمونه برای همیشه برای همان دو نمونه یکسان خواهد بود. - اگر ویژگی عمومی نوع تغییر کند، به «ترکیببندی» اطلاع داده خواهد شد.
- همه انواع ویژگیهای عمومی نیز پایدار هستند.
برخیاز انواع رایج مهم که در این قرارداد قرار میگیرند و کامپایلر Compose آنها را بهعنوان پایدار درنظر میگیرد، حتی اگر بهصراحت بااستفاده از حاشیهنویسی @Stable بهعنوان پایدار علامتگذاری نشده باشند:
- همه انواع مقدار اولیه:
Boolean،Int،Long،Float،Char، و غیره. - رشتهها
- همه انواع تابع (لامبدا)
همه این انواع میتوانند از قرارداد پایدار پیروی کنند زیرا تغییرناپذیر هستند. ازآنجاییکه انواع تغییرناپذیر هرگز تغییر نمیکنند، هرگز لازم نیست تغییر را به «ترکیببندی» اطلاع دهند، بنابراین پیروی از این قرارداد بسیار آسانتر است.
یک نوع قابلتوجه که پایدار است اما تغییرپذیر است، نوع MutableState
در Compose است. If a value is held in a MutableState, the state object overall is
considered to be stable as Compose will be notified of any changes to the
.value property of State.
وقتی همه انواع ارسالشده بهعنوان پارامتر به یک composable پایدار باشند، مقادیر پارامتر براساس موقعیت 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 همه پیادهسازیهای آن را بهعنوان پایدار درنظر میگیرد.
توصیهشده برای شما
- توجه: وقتی جاوا اسکریپت خاموش باشد، نوشتار پیوند نمایش داده میشود
- حالت و Jetpack Compose
- عوارض جانبی در Compose
- ذخیره کردن وضعیت میانای کاربر در Compose