Performans sorunlarına neden olan kararsız bir sınıfla karşılaştığınızda, Compose'un bu sınıfı nasıl karşılaştırdığını veya örneklendirdiğini optimize etmeniz gerekir. Bu belgede, bunu yapmak için kullanabileceğiniz çeşitli teknikler açıklanmaktadır.
Güçlü atlamayı etkinleştirme
Güçlü atlama modu, Kotlin 2.0.20'den itibaren varsayılan olarak etkindir.
Güçlü atlama modu, kararsız parametreleri örnek eşitliği (===) kullanarak karşılaştırarak kararsız parametreleri olan composable'ların atlanmasına olanak tanır. Kararlı parametreler ise yapısal eşitlik (Object.equals()) kullanılarak karşılaştırılmaya devam eder.
2.0.20'den eski bir Kotlin sürümü kullanıyorsanız önce güçlü atlama modunu etkinleştirin. Güçlü atlama modu zaten etkinse ve bir composable hâlâ gereksiz yere yeniden oluşturuluyorsa her yeniden oluşturma geçişinde kararsız parametrelerin yeni örneklerinin ayrılıp ayrılmadığını kontrol edin.
Daha fazla bilgi için Güçlü atlama bölümüne bakın.
Sınıfı sabit hale getirme
Kararsız bir sınıfı tamamen sabit hale getirerek derleyicinin kararlılığını otomatik olarak tahmin etmesini de sağlayabilirsiniz.
- Değişmez: Bu türün bir örneği oluşturulduktan sonra hiçbir özelliğin değerinin asla değişemeyeceği ve tüm yöntemlerin referans açısından şeffaf olduğu bir türü belirtir.
- Sınıfın tüm özelliklerinin
varyerinevalolduğundan ve sabit türde olduğundan emin olun. String,IntveFloatgibi temel türler her zaman sabittir.- Oluşturma derleyicisiyle oluşturulan modüllerdeki
valtemel veyaStringözelliklere sahip düzdata classtürleri zaten kararlı olarak tahmin edilir ve otomatik olarak.equals()ile karşılaştırılır. Bunlara@Immutableile ek açıklama eklemeniz gerekmez. - Değişmezlik mümkün değilse tüm değişken özellikler için Compose durumunu kullanmanız gerekir.
- Sınıfın tüm özelliklerinin
- Kararlı: Değişken bir türü belirtir. Compose çalışma zamanı, türün herkese açık özelliklerinden veya yöntem davranışlarından herhangi birinin önceki bir çağrıdan farklı sonuçlar verip vermeyeceğini ve ne zaman vereceğini fark etmez.
Koleksiyonlar ve kararlılık
Compose'un bir sınıfı kararsız olarak değerlendirmesinin yaygın nedenlerinden biri koleksiyonlardır. Kararlılık sorunlarını teşhis etme sayfasında belirtildiği gibi, Compose derleyicisi List, Map ve Set gibi koleksiyon arayüzlerinin gerçekten sabit olduğundan tamamen emin olamaz ve bu nedenle bunları kararsız olarak işaretler.
Kararlılık sorunlarını teşhis etme kılavuzundaki şu sınıfı ele alalım:
unstable class Snack {
…
unstable val tags: Set<String>
…
}
Güçlü atlama modu etkinleştirildiğinde (Kotlin 2.0.20 ve sonraki sürümlerde varsayılan olarak etkindir) kararsız koleksiyonlar ve koleksiyon içeren sınıflar, aynı referans her iletildiğinde örnek eşitliğini (===) kullanarak yeniden oluşturma işlemini zaten atlar.
kotlin.collections.List, Set veya Map yerine Kotlinx Immutable Collections (ImmutableList, ImmutableSet, ImmutableMap) kullanmadan ya da koleksiyon içeren modellere @Immutable, @Stable veya stability_config.conf kuralları eklemeden önce dikkatli olun. Güçlü atlama modunda standart List, referans eşitliğini (===) kullanarak atlama yaparken ImmutableList ve ek açıklamalı koleksiyon modelleri, her yeniden oluşturma aşamasında öğe bazında .equals() karşılaştırmalar yapar.
Büyük koleksiyonlar veya geç yüklenen düzenler (LazyColumn, LazyRow, LazyGrid) için her öğede .equals() kontrolü yapmak, yeniden oluşturmaktan daha uzun sürebilir. ImmutableList özelliğini data class öğesinden alt composable öğesine geçirirseniz bu maliyet iki katına çıkar:
data class SnackCategory(
val name: String,
val snacks: ImmutableList<Snack>
)
@Composable
fun CategorySection(category: SnackCategory) {
Text(category.name)
// Compose compares category.snacks twice: once in SnackCategory.equals()
// for CategorySection, and again for HighlightedSnacks.
HighlightedSnacks(snacks = category.snacks)
}
Koleksiyonlarda yapısal eşitliği zorunlu kılmak yerine, koleksiyonun örnek referansını emisyonlar arasında koruyun (örneğin, dönüşümleri ViewModel içinde veya remember ile önbelleğe alarak). Böylece Compose, örnek eşitliğini (===) kullanmayı atlayabilir. Koleksiyon içeren modellerde ImmutableList veya kararlılık ek açıklamalarını yalnızca koleksiyonun örnek referansını koruyamadığınızda ve karşılaştırmalar, yapısal eşitlik kontrollerinin yeniden oluşturmadan daha hızlı olduğunu doğruladığında kullanın.
2.0.20'den eski bir Kotlin sürümü kullanıyorsanız önce Güçlü atlama modunu etkinleştirin.
Stable veya Immutable ile ek açıklama ekleyin
Düşük maliyetli
.equals() kontrollerle küçük sınıflardaki kararlılık sorunlarını çözmenin olası bir yolu, kararsız sınıflara @Stable veya
@Immutable ile ek açıklama eklemektir.
Bir sınıfı ek açıklamayla belirtmek, derleyicinin sınıfınız hakkında çıkaracağı bilgileri geçersiz kılar. !!, Kotlin'deki operatöre benzer. Bu ek açıklamaları nasıl kullandığınız konusunda çok dikkatli olmanız gerekir. Derleyici davranışını geçersiz kılmak, composable'ınızın beklediğiniz zaman yeniden oluşturulmaması gibi öngörülemeyen hatalara yol açabilir.
Güçlü atlama modu etkinleştirildiğinde, @Stable veya @Immutable ekleyip eklemeyeceğinize karar verirken aşağıdaki yönergeleri uygulayın:
- Zaten kararlı olarak tahmin edilen sınıflara ek açıklama eklemeyin: Compose derleyicisiyle oluşturulan modüllerde kararlı türlerin
valözelliklerine sahip sınıflar zaten kararlı olarak tahmin edilir. - Yalnızca örnekler yeniden oluşturulduğunda sınıflara düşük maliyetli
.equals()kontrolleri ekleyin: Derleyici kararlılığı tahmin edemediğinde (ör. Compose dışı modüllerden arayüzler veya sınıflar)@Stableya da@Immutableekleyin ve sınıf küçükse ve düşük maliyetli bir.equals()kontrolü varsa veri kaynağı her yayında değişmeyen öğeler için yeni nesne örnekleri ayırır. - Koleksiyon içeren modellerde dikkatli olun: Büyük koleksiyonlar (
List,Set,Map) içeren veri modellerine@Stableveya@Immutableeklemekten kaçının. Bu durum özellikle geç yüklenen düzenlerde (LazyColumn,LazyRow,LazyGrid,Pager) kullanılan modeller için geçerlidir. Bu modelleri ek açıklama olmadan bırakmak, Compose'un her öğe için yapısal.equals()kontrolleri çalıştırmak yerine örnek eşitliğini (===) kullanarak modelleri karşılaştırmasına olanak tanır.
Aşağıdaki snippet, derleyicinin kararlılığı tahmin edemediği bir modülde tanımlandığında değişmez olarak ek açıklama eklenmiş düz bir veri sınıfının (koleksiyon özellikleri olmadan) minimum örneğini sağlar:
@Immutable
data class SnackSummary(
val id: Long,
val name: String,
val price: Long
)
@Immutable veya @Stable ek açıklamasını kullanmanız fark etmeksizin Compose derleyici, SnackSummary sınıfını kararlı olarak işaretler ve Object.equals() kullanarak karşılaştırır.
Koleksiyonlardaki ek açıklamalı sınıflar
List<Snack> türünde bir parametre içeren bir composable düşünün:
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
stable index: Int
unstable snacks: List<Snack>
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
)
Snack kararlı olsa veya @Immutable ile ek açıklama eklenmiş olsa bile Compose derleyici, List bir arayüz olduğu için HighlightedSnacks içindeki snacks parametresini unstable olarak işaretler.
Güçlü atlama modu etkinleştirildiğinde HighlightedSnacks, skippable olarak işaretlenmeye devam eder ve aynı List örneği iletildiğinde örnek eşitliği (===) kullanılarak yeniden oluşturma atlanır. List<Snack> öğesini bir @Immutable sarmalayıcı sınıfına sarmalayarak, List<Snack> öğesini ImmutableList<Snack> ile değiştirerek veya kararlılık yapılandırma dosyanıza kotlin.collections.* ekleyerek snacks parametresini stable yapabilirsiniz.
Ancak bu durumda Compose, her yeniden oluşturma aşamasında öğe öğe .equals()
karşılaştırma yapar. Bu yaklaşımları yalnızca listenin örnek referansını koruyamadığınızda ve karşılaştırmalar bir avantaj gösterdiğinde kullanın.
Kararlılık yapılandırma dosyası
Compose Derleyici 1.5.5'ten itibaren, kararlı olarak kabul edilecek sınıfların yapılandırma dosyası derleme zamanında sağlanabilir. Bu sayede, kontrolünüzde olmayan sınıfları (ör. LocalDateTime gibi standart kitaplık sınıfları) kararlı olarak değerlendirebilirsiniz.
Yapılandırma dosyası, her satırda bir sınıf bulunan bir düz metin dosyasıdır. Yorumlar, tekli ve çiftli joker karakterler desteklenir.
Örnek yapılandırma:
// 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<*,_>
Bu özelliği etkinleştirmek için yapılandırma dosyasının yolunu Compose derleyici Gradle eklentisi yapılandırmasının composeCompiler seçenekler bloğuna iletin.
composeCompiler {
stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}
Compose derleyici, projenizdeki her modülde ayrı ayrı çalıştığından, gerekirse farklı modüllere farklı yapılandırmalar sağlayabilirsiniz. Alternatif olarak, projenizin kök düzeyinde bir yapılandırma oluşturup bu yolu her modüle iletebilirsiniz.
Birden çok modül
Sık karşılaşılan bir diğer sorun da çok modüllü mimariyle ilgilidir. Compose derleyicisi, bir sınıfın kararlı olup olmadığını yalnızca, sınıfın referans verdiği tüm ilkel olmayan türler açıkça kararlı olarak işaretlenmişse veya Compose derleyicisiyle oluşturulmuş bir modüldeyse anlayabilir.
Veri katmanınız, kullanıcı arayüzü katmanınızdan ayrı bir modüldeyse (önerilen yaklaşım budur) bu sorunla karşılaşabilirsiniz. Güçlü atlama modunda, Compose dışı modüllerdeki sınıflar kararsız olarak değerlendirilir ve aynı örnek
iletildiğinde örnek eşitliği (===) kullanılarak yeniden oluşturma işlemi yine de
atlanır. Ancak veri katmanınız her emisyonda aynı değerlere sahip küçük, düz modellerin yeni örneklerini sık sık tahsis ediyorsa yapısal eşitliği (Object.equals()) etkinleştirebilirsiniz.
Çözüm
Bu sorunu çözmek için aşağıdaki yaklaşımlardan birini uygulayabilirsiniz:
- Derleyici yapılandırma dosyanıza ucuz
.equals()kontrolleri içeren küçük, düz sınıflar ekleyin. - Veri katmanı modüllerinizde Compose derleyicisini etkinleştirin veya sınıflarınızı uygun yerlerde
@Stableya da@Immutableile etiketleyin.- Bu işlem, veri katmanınıza bir Compose bağımlılığı eklemeyi içerir. Ancak bu, yalnızca Compose çalışma zamanı için bağımlılıktır,
Compose-UIiçin değildir.
- Bu işlem, veri katmanınıza bir Compose bağımlılığı eklemeyi içerir. Ancak bu, yalnızca Compose çalışma zamanı için bağımlılıktır,
- Kullanıcı arayüzü modülünüzde, veri katmanı sınıflarınızı kullanıcı arayüzüne özel sarmalayıcı sınıflara sarmalayın.
Aynı sorun, harici kitaplıklar kullanılırken de ortaya çıkar.
Her composable atlanabilir olmamalıdır
Kararlılıkla ilgili sorunları düzeltmeye çalışırken her composable'ı atlanabilir hale getirmeye veya her parametrede yapısal eşitliği zorunlu kılmaya çalışmayın. Oluşturulabilir bir öğeyi atlamak ücretsiz değildir: Atlanabilir bir oluşturulabilir öğenin her parametresi, Compose çalışma zamanı tarafından hatırlanmış gibi depolanır ve Compose, atlanması gerekip gerekmediğini belirlemek için işlev her yeniden oluşturulduğunda her parametreyi önceki değeriyle karşılaştırmalıdır. Her yerde atlamayı zorlamaya çalışmak, tasarruf ettiğinden daha fazla ek yük getiren erken optimizasyona yol açabilir.
Yapısal kararlılığı zorunlu kılmanın gerçek bir fayda sağlamadığı ve performansı veya sürdürülebilirliği düşürebileceği birçok durum vardır. Örneğin:
- Sık sık veya hiç yeniden oluşturulmayan bir composable.
- Yalnızca atlanabilir composable'ları çağıran bir composable.
- Çok sayıda parametre içeren bir composable veya pahalı
.equals()uygulamaları içeren koleksiyon modelleri. Bu durumda, herhangi bir parametrenin değişip değişmediğini kontrol etmenin maliyeti, ucuz bir yeniden oluşturma maliyetinden daha fazla olabilir.
Yalnızca yeniden oluşturma sayılarına çok fazla odaklanmayın. Oluşturulabilir öğeleri hafif hale getirmeye, hızla değişen durum okumalarını Düzen veya Çizim aşamalarına ertelemeye ve değişiklikleri karşılaştırmalarla doğrulamaya odaklanın. Atlanabilir bir composable, küçük bir ek yük ekler ve bu ek yük, buna değmeyebilir. Yeniden başlatılabilir veya atlanabilir olmanın değerinden daha fazla ek yük getirdiğini belirlediğiniz durumlarda, composable'ınızı yeniden başlatılamaz veya atlanamaz olarak da ek açıklama ekleyebilirsiniz.