حالت رد شدن قوی

«رد شدن قوی» حالتی است که در کامپایلر Compose دردسترس است. وقتی فعال باشد، رفتار کامپایلر را به دو روش تغییر می‌دهد:

فعال کردن حالت رد شدن قوی

رد شدن قوی به‌طور پیش‌فرض در Kotlin 2.0.20 فعال است.

برای فعال کردن رد شدن قوی برای یک ماژول Gradle در نسخه‌ای قبل‌از 2.0.20، گزینه زیر را در بلوک composeCompiler پیکربندی Gradle خود اضافه کنید:

android { ... }

composeCompiler {
   enableStrongSkippingMode = true
}

قابلیت رد کردن ساختنی

حالت پرش قوی برخی‌از قوانین پایداری را که معمولاً توسط کامپایلر Compose درخصوص پرش و توابع ترکیب‌پذیر اعمال می‌شود، کاهش می‌دهد. به‌طور پیش‌فرض، اگر همه آرگومان‌های تابع ترکیب‌پذیر مقادیر پایداری داشته باشند، کامپایلر Compose آن را به‌عنوان قابل‌ردشدن علامت‌گذاری می‌کند. حالت پرش قوی این را تغییر می‌دهد.

با فعال شدن رد شدن قوی، همه توابع ترکیب‌پذیر قابل‌راه‌اندازی مجدد، قابل‌رد شدن می‌شوند. این موضوع صرف‌نظر از اینکه پارامترهای ناپایدار داشته باشند یا نه، صدق می‌کند. توابع ترکیب‌پذیر غیرقابل‌راه‌اندازی مجدد همچنان غیرقابل‌ردشدن باقی می‌مانند.

چه زمانی رد کنیم

برای تعیین اینکه آیا باید از یک composable درطول ترکیب‌بندی مجدد صرف‌نظر شود یا نه، Compose مقدار هر پارامتر را با مقادیر قبلی آن‌ها مقایسه می‌کند. نوع مقایسه به پایداری پارامتر بستگی دارد.

  • پارامترهای ناپایدار بااستفاده از برابری نمونه (===) مقایسه می‌شوند
  • پارامترهای پایدار بااستفاده از برابری شیء (Object.equals()) مقایسه می‌شوند

اگر همه پارامترها این الزامات را برآورده کنند، Compose از composable درطول بازترکیب‌بندی صرف‌نظر می‌کند.

ممکن است بخواهید یک composable از پرش قوی انصراف دهد. یعنی ممکن است بخواهید یک composable قابل‌راه‌اندازی مجدد اما غیرقابل‌ردشدن داشته باشید. در این مورد، از حاشیه‌نویسی @NonSkippableComposable استفاده کنید.

@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}

حاشیه‌نویسی کلاس‌ها به‌عنوان پایدار

اگر می‌خواهید شیئی بااستفاده از برابری شیء (Object.equals()) به‌جای برابری نمونه (===) مقایسه شود، به حاشیه‌نویسی کلاس ارائه‌شده با @Stable یا @Immutable ادامه دهید.

قبل‌از افزودن @Stable یا @Immutable، موازنه‌های زیر را درنظر داشته باشید:

  • کلاس‌های دارای ویژگی‌های پایدار در ماژول‌های Compose ازقبل پایدار هستند: کلاسی با ویژگی‌های val از نوع پایدار در ماژولی که با کامپایلر Compose کامپایل شده است به‌طور خودکار به‌عنوان پایدار استنباط می‌شود و از .equals() بدون نیاز به @Immutable یا @Stable استفاده می‌کند.
  • فقط کلاس‌ها را با بررسی‌های ارزان .equals() حاشیه‌نویسی کنید: وقتی کامپایلر نمی‌تواند پایداری را استنباط کند (مانند کلاس‌های ماژول‌های غیرCompose) و نمونه‌های جدید با مقادیر یکسان به‌طور مکرر اختصاص داده می‌شوند، @Stable یا @Immutable را اضافه کنید، مشروط بر اینکه بررسی .equals() کلاس ارزان باشد.
  • هنگام حاشیه‌نویسی مدل‌ها با مجموعه‌ها احتیاط کنید: افزودن @Stable یا @Immutable به مدل‌های حاوی مجموعه‌ها (یا جایگزین کردن List با ImmutableList) باعث می‌شود Compose هر عنصر در مجموعه را با .equals() در هر گذر بازترکیب مقایسه کند. برای مجموعه‌ها یا مدل‌های بزرگ استفاده‌شده در چیدمان‌های تنبل (LazyColumn،‏ LazyRow،‏ LazyGrid)، مقایسه هر عنصر می‌تواند کندتر از استفاده از برابری نمونه (===) یا بازترکیب مستقیم باشد.

یادسپاری لامبدا

حالت پرش قوی همچنین امکان یادسپاری بیشتر لامبداها را در داخل ترکیب‌پذیرها فراهم می‌کند. با فعال شدن پرش قوی، هر لامبدا در یک تابع ترکیب‌پذیر به‌طور خودکار به‌خاطر سپرده می‌شود.

مثال‌ها

برای دستیابی به یادسپاری لامبداها در داخل composable هنگام استفاده از پرش قوی، کامپایلر لامبدا شما را با یک فراخوانی remember می‌پیچد. این ویژگی با ضبط‌های لامبدا کلیدگذاری شده است.

موردی را درنظر بگیرید که در آن یک لامبدا مانند مثال زیر دارید:

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = {
        use(unstableObject)
        use(stableObject)
    }
}

With strong skipping enabled, the compiler memoizes the lambda by wrapping it in a remember call:

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = remember(unstableObject, stableObject) {
        {
            use(unstableObject)
            use(stableObject)
        }
    }
}

کلیدها از همان قوانین مقایسه توابع ترکیب‌پذیر پیروی می‌کنند. زمان اجرا کلیدهای ناپایدار را بااستفاده از برابری نمونه مقایسه می‌کند. این روش کلیدهای پایدار را بااستفاده از برابری شیء مقایسه می‌کند.

به‌خاطرسپاری و بازترکیب

این بهینه‌سازی تعداد ترکیب‌پذیرهایی را که زمان اجرا درطول بازترکیب رد می‌کند به‌طور قابل‌توجهی افزایش می‌دهد. بدون یادسپاری، احتمال اینکه زمان اجرا یک لامبدا جدید را به هر ترکیب‌پذیری که پارامتر لامبدا را درطول بازترکیب می‌گیرد اختصاص دهد بسیار بیشتر است. درنتیجه، LAMBDA جدید پارامترهایی دارد که با ترکیب‌بندی آخر برابر نیستند. این امر منجر به بازترکیب می‌شود.

اجتناب از یادسپاری

اگر لامبدایی دارید که نمی‌خواهید آن را یادداشت کنید، از حاشیه‌نویسی @DontMemoize استفاده کنید.

val lambda = @DontMemoize {
    ...
}

اندازه APK

هنگام کامپایل، «ترکیب‌شدنی‌های» قابل‌ردشدن نسبت به ترکیب‌شدنی‌های غیرقابل‌ردشدن، کد تولیدشده بیشتری ایجاد می‌کنند. با فعال شدن رد شدن قوی، کامپایلر تقریباً همه composableها را به‌عنوان قابل‌ردشدن علامت‌گذاری می‌کند و همه لامبداها را در یک remember{...} می‌پیچد. به همین دلیل، فعال کردن حالت رد شدن قوی تأثیر بسیار کمی بر اندازه APK برنامه شما دارد.

فعال کردن پرش قوی در اکنون در Android اندازه APK را ۴ کیلوبایت افزایش داد. تفاوت در اندازه تا حد زیادی به تعداد ترکیب‌پذیرهای قبلاً غیرقابل‌ردشدن موجود در برنامه موردنظر بستگی دارد، اما باید نسبتاً جزئی باشد.