رفع مشکلات پایداری

هنگامی که با یک کلاس ناپایدار مواجه می شوید که باعث مشکلات عملکرد می شود، باید آن را پایدار کنید. این سند چندین تکنیک را که می توانید برای انجام این کار استفاده کنید، تشریح می کند.

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

ابتدا باید سعی کنید حالت پرش قوی را فعال کنید. حالت پرش قوی امکان نادیده گرفتن مواد ترکیبی با پارامترهای ناپایدار را فراهم می کند و ساده ترین روش برای رفع مشکلات عملکرد ناشی از پایداری است.

برای اطلاعات بیشتر به پرش قوی مراجعه کنید.

کلاس را تغییرناپذیر کنید

همچنین می توانید سعی کنید یک کلاس ناپایدار را کاملاً تغییرناپذیر کنید.

  • Immutable : نوعی را نشان می دهد که در آن ارزش هیچ ویژگی پس از ساخته شدن نمونه ای از آن نوع هرگز نمی تواند تغییر کند و همه متدها به صورت ارجاعی شفاف هستند.
    • مطمئن شوید که تمام خصوصیات کلاس هم val هستند نه var و هم از انواع تغییرناپذیر.
    • انواع اولیه مانند String, Int و Float همیشه تغییر ناپذیر هستند.
    • اگر این غیرممکن است، باید از حالت Compose برای هر ویژگی قابل تغییر استفاده کنید.
  • Stable : نشان دهنده نوعی است که قابل تغییر است. زمان اجرا Compose متوجه نمی شود که آیا و زمانی که هر یک از ویژگی های عمومی نوع یا رفتار روش نتایج متفاوتی از فراخوانی قبلی داشته باشد یا خیر.

مجموعه های تغییرناپذیر

دلیل رایجی که Compose یک کلاس را ناپایدار می‌داند مجموعه‌ها هستند. همانطور که در صفحه تشخیص مشکلات پایداری ذکر شد، کامپایلر Compose نمی‌تواند کاملاً مطمئن باشد که مجموعه‌هایی مانند List, Map و Set واقعاً تغییرناپذیر هستند و بنابراین آنها را به عنوان ناپایدار علامت‌گذاری می‌کند.

برای حل این مشکل، می توانید از مجموعه های غیرقابل تغییر استفاده کنید. کامپایلر Compose شامل پشتیبانی از Kotlinx Immutable Collections است. این مجموعه‌ها تضمین شده‌اند که تغییر ناپذیر هستند و کامپایلر Compose با آنها رفتار می‌کند. این کتابخانه هنوز در حالت آلفا است، بنابراین منتظر تغییرات احتمالی در API آن باشید.

دوباره این کلاس ناپایدار را از راهنمای مسائل مربوط به پایداری تشخیص در نظر بگیرید:

unstable class Snack {
  
  unstable val tags: Set<String>
  
}

شما می توانید tags با استفاده از یک مجموعه غیرقابل تغییر پایدار کنید. در کلاس، نوع tags را به ImmutableSet<String> تغییر دهید:

data class Snack{
    
    val tags: ImmutableSet<String> = persistentSetOf()
    
}

پس از انجام این کار، تمام پارامترهای کلاس غیرقابل تغییر هستند و کامپایلر Compose کلاس را به عنوان پایدار علامت گذاری می کند.

حاشیه نویسی با Stable یا Immutable

یک مسیر ممکن برای حل مشکلات پایداری، حاشیه‌نویسی کلاس‌های ناپایدار با @Stable یا @Immutable است.

حاشیه نویسی یک کلاس، آنچه را که کامپایلر درباره کلاس شما استنباط می کند، نادیده می گیرد. شبیه به !! اپراتور در کاتلین شما باید در مورد نحوه استفاده از این حاشیه نویسی بسیار مراقب باشید. نادیده گرفتن رفتار کامپایلر می‌تواند شما را به باگ‌های پیش‌بینی‌نشده‌ای سوق دهد، مانند اینکه composable شما در زمانی که انتظار دارید دوباره ترکیب نشود.

اگر می‌توانید کلاس خود را بدون حاشیه‌نویسی پایدار کنید، باید از این طریق برای رسیدن به ثبات تلاش کنید.

قطعه زیر یک نمونه حداقلی از یک کلاس داده را ارائه می دهد که به عنوان غیرقابل تغییر توضیح داده شده است:

@Immutable
data class Snack(

)

چه از حاشیه‌نویسی @Immutable یا @Stable استفاده کنید، کامپایلر Compose کلاس Snack را به‌عنوان پایدار علامت‌گذاری می‌کند.

کلاس های مشروح در مجموعه ها

یک Composable را در نظر بگیرید که شامل پارامتری از نوع List<Snack> باشد:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  
  unstable snacks: List<Snack>
  
)

حتی اگر Snack با @Immutable حاشیه‌نویسی کنید، کامپایلر Compose همچنان پارامتر snacks را در HighlightedSnacks به‌عنوان ناپایدار علامت‌گذاری می‌کند.

وقتی صحبت از انواع مجموعه می شود، پارامترها با مشکل مشابه کلاس ها مواجه هستند، کامپایلر Compose همیشه پارامتری از نوع List به عنوان ناپایدار علامت گذاری می کند ، حتی زمانی که مجموعه ای از انواع پایدار باشد.

شما نمی توانید یک پارامتر جداگانه را به عنوان پایدار علامت گذاری کنید، و همچنین نمی توانید یک قابل ترکیب را حاشیه نویسی کنید تا همیشه قابل رد شدن باشد. چندین مسیر رو به جلو وجود دارد.

راه های مختلفی وجود دارد که می توانید مشکل مجموعه های ناپایدار را حل کنید. بخش‌های فرعی زیر این رویکردهای مختلف را تشریح می‌کنند.

فایل پیکربندی

اگر از قرارداد پایداری در پایگاه کد خود راضی هستید، می‌توانید با افزودن kotlin.collections.* به فایل پیکربندی پایداری، مجموعه‌های Kotlin را پایدار در نظر بگیرید.

مجموعه تغییرناپذیر

برای کامپایل ایمنی تغییرناپذیری زمان، می توانید به جای List از یک مجموعه تغییرناپذیر kotlinx استفاده کنید.

@Composable
private fun HighlightedSnacks(
    
    snacks: ImmutableList<Snack>,
    
)

لفاف

اگر نمی توانید از یک مجموعه تغییرناپذیر استفاده کنید، می توانید مجموعه خود را بسازید. برای انجام این کار، List را در یک کلاس پایدار مشروح قرار دهید. بسته به نیاز شما، بسته بندی عمومی احتمالا بهترین انتخاب برای این کار است.

@Immutable
data class SnackCollection(
   val snacks: List<Snack>
)

سپس می توانید از این به عنوان نوع پارامتر در composable خود استفاده کنید.

@Composable
private fun HighlightedSnacks(
    index: Int,
    snacks: SnackCollection,
    onSnackClick: (Long) -> Unit,
    modifier: Modifier = Modifier
)

راه حل

پس از اتخاذ هر یک از این رویکردها، کامپایلر Compose اکنون HighlightedSnacks Composable را به عنوان skippable و restartable علامت گذاری می کند.

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  stable snacks: ImmutableList<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

در طول ترکیب مجدد، Compose اکنون می‌تواند از HighlightedSnacks صرفنظر کند، اگر هیچ یک از ورودی‌های آن تغییر نکرده باشد.

فایل پیکربندی پایداری

با شروع Compose Compiler 1.5.5، یک فایل پیکربندی از کلاس ها برای در نظر گرفتن پایدار می تواند در زمان کامپایل ارائه شود. این اجازه می‌دهد تا کلاس‌هایی را که کنترل نمی‌کنید، مانند کلاس‌های کتابخانه استاندارد مانند LocalDateTime ، به‌عنوان پایدار در نظر بگیرید.

فایل پیکربندی یک فایل متنی ساده با یک کلاس در هر ردیف است. نظرات، تک، و دو علامت عام پشتیبانی می شوند. یک نمونه پیکربندی:

// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider my datalayer stable
com.datalayer.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>

برای فعال کردن این ویژگی، مسیر فایل پیکربندی را به بلوک گزینه های composeCompiler از پیکربندی افزونه Compose compiler Gradle منتقل کنید.

composeCompiler {
  stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}

از آنجایی که کامپایلر Compose بر روی هر ماژول پروژه شما به طور جداگانه اجرا می شود، در صورت نیاز می توانید تنظیمات مختلفی را برای ماژول های مختلف ارائه دهید. از طرف دیگر، یک پیکربندی در سطح ریشه پروژه خود داشته باشید و آن مسیر را به هر ماژول منتقل کنید.

ماژول های متعدد

یکی دیگر از مسائل رایج مربوط به معماری چند ماژول است. کامپایلر Compose تنها زمانی می‌تواند استنباط کند که یک کلاس پایدار است یا خیر که همه انواع غیر ابتدایی که به آنها ارجاع می‌دهد یا به صراحت به‌عنوان پایدار علامت‌گذاری شده باشند یا در ماژولی که با کامپایلر Compose نیز ساخته شده است.

اگر لایه داده شما در یک ماژول جداگانه از لایه UI شما قرار دارد، که رویکرد توصیه شده است، ممکن است با این مشکل مواجه شوید.

راه حل

برای حل این مشکل می توانید یکی از روش های زیر را در پیش بگیرید:

  1. کلاس ها را به فایل پیکربندی کامپایلر خود اضافه کنید.
  2. کامپایلر Compose را در ماژول های لایه داده خود فعال کنید یا کلاس های خود را در صورت لزوم با @Stable یا @Immutable تگ کنید.
    • این شامل افزودن وابستگی Compose به لایه داده شما است. با این حال، این فقط وابستگی برای زمان اجرا Compose است و نه برای Compose-UI .
  3. در ماژول UI خود، کلاس های لایه داده خود را در کلاس های wrapper مخصوص UI قرار دهید.

هنگام استفاده از کتابخانه های خارجی در صورتی که از کامپایلر Compose استفاده نمی کنند، همین مشکل نیز رخ می دهد.

نباید هر آهنگسازی را رد کرد

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

موقعیت‌های زیادی وجود دارد که قابل پرش بودن هیچ مزیت واقعی ندارد و می‌تواند به سختی حفظ کد منجر شود. به عنوان مثال:

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

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