Jetpack Compose compiler raporlarını Macrobenchmark sonuçlarıyla eşleyerek unstable parametreleri, koleksiyonları ve lambda capture'larını teşhis edin. Değişikliği P50/P90 kare süresiyle doğrulayın.
Jetpack Compose'da Stability Raporlarıyla Gereksiz Yeniden Çalışmayı Azaltmak
Jetpack Compose compiler raporlarını üretin ve okunabilir hale getirin
Jetpack Compose'da bir composable'ın yeniden çalışmasının nedeni çoğu zaman çağıran taraftaki state değil, parametre tipinin compiler tarafından unstable kabul edilmesidir. Tahmin etmek yerine Compose Compiler'ın metrics ve reports çıktısını CI'da üretin. Kotlin Compose compiler eklentisini kullanan projelerde aşağıdaki yapılandırma, her modül için `composables.txt`, `composables.csv` ve sınıf stability raporlarını `build/compose_compiler` altında toplar.
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
composeCompiler {
reportsDestination = layout.buildDirectory.dir("compose_compiler/reports")
metricsDestination = layout.buildDirectory.dir("compose_compiler/metrics")
stabilityConfigurationFile = rootProject.layout.projectDirectory
.file("compose-stability.conf")
}Raporları üretmek için temiz bir derleme alın: `./gradlew :app:clean :app:assembleDebug`. `composables.txt` içindeki `restartable`, `skippable` ve `unstable` işaretlerini aynı satırda okuyun. `restartable` olması fonksiyonun state değişiminde yeniden başlatılabildiğini, `skippable` olması ise parametreleri değişmediyse çağrının atlanabildiğini gösterir. Kritik ayrıntı şudur: Bir composable `restartable` fakat `skippable` değilse, üst parent yeniden çalıştığında kendi parametreleri eşit kalsa bile gövdesi yeniden yürütülebilir. Bu nedenle ilk hedef, tüm ekranı değil, kaydırılabilir liste satırları ve pahalı çizim yapan alt ağaçlardaki unstable parametreleri bulmaktır.
`compose-stability.conf` dosyasına sadece gerçekten immutable olan, kaynak kodunu kontrol ettiğiniz türleri ekleyin. Örneğin üçüncü taraf bir SDK'nın model paketini körlemesine eklemek, SDK nesnesi bellek içinde değiştiğinde Compose'un eski UI'ı göstermesine yol açabilir. Konfigürasyon dosyasını kod incelemesinde API sözleşmesi gibi ele alın.
# compose-stability.conf
# Bu tipin alanları uygulama tarafından değiştirilmiyor olmalı.
com.example.feature.feed.FeedId
# İkinci generic parametre unstable kabul edilir, ilki dikkate alınmaz.
com.example.core.UiResult<*,_>Android MVVM mimarisi içinde immutable UI state sınırını kurun
Android MVVM mimarisi kullanan bir ekranda `ViewModel` sınırından mutable koleksiyon veya framework nesnesi geçirmek, Compose raporlarında zincirleme unstable parametre üretir. Özellikle Kotlin'in `Listimport androidx.compose.runtime.Immutable
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Immutable
data class FeedUiState(
val articles: ImmutableList<ArticleUi> = persistentListOf(),
val isRefreshing: Boolean = false
)
@Immutable
data class ArticleUi(
val id: String,
val title: String,
val read: Boolean
)
fun markRead(id: String) {
_uiState.update { state ->
state.copy(
articles = state.articles.map {
if (it.id == id) it.copy(read = true) else it
}.toImmutableList()
)
}
}
Buradaki `@Immutable`, runtime'da derin immutability kontrolü yapan bir doğrulayıcı değildir; compiler'a verilen bir sözdür. `ArticleUi` içine sonradan `var selected: Boolean` veya `MutableState<Boolean>` eklerseniz annotation'ı kaldırın ya da modeli yeniden tasarlayın. Aksi halde Compose parametreyi güvenle skip edebilir, fakat değişen alan için yeniden çizim tetiklenmeyebilir. Rapor değişikliğini doğrulamak için `build/compose_compiler/reports` altındaki ilgili satırın `unstable FeedUiState` yerine stable veya skippable çağrı olarak değiştiğini kontrol edin.
Repository katmanından gelen `Flow<List<Entity>>` değerini doğrudan UI'a taşımayın. `stateIn` sonrası mapping işlemini ViewModel'de yapıp `ImmutableList` üretmek, hem domain entity sızıntısını önler hem de Compose'a küçük ve açık bir render sözleşmesi verir. Bu ayrım, android eğitimi veya kotlin eğitimi materyallerinde sık görülen `data class kullanmak yeterlidir` yaklaşımından daha katıdır: data class yalnızca eşitlik üretir, içindeki mutable alanları immutable yapmaz.
Jetpack Compose listelerinde lambda capture ve item kimliğini ölçülebilir tasarlayın
Stability sorunu yalnızca modelden çıkmaz. Her yeniden çalışmada oluşturulan callback'ler, capture edilen nesne unstable ise alt composable'ların skip edilmesini engelleyebilir. Aşağıdaki örnekte `navigator` ömrü composable ağacından uzunsa ve referansı değişmiyorsa callback'i `remember` ile sabitlemek anlamlıdır. `navigator` değişebiliyorsa anahtara eklenmelidir; boş anahtarlı `remember` kullanmak eski navigator referansını çağıran, zor yakalanan bir navigation hatası üretir.
@Composable
fun FeedScreen(
state: FeedUiState,
navigator: FeedNavigator
) {
val onArticleClick = remember(navigator) {
{ id: String -> navigator.openArticle(id) }
}
LazyColumn {
items(
items = state.articles,
key = { article -> article.id },
contentType = { "article" }
) { article ->
ArticleRow(article = article, onClick = onArticleClick)
}
}
}`key` eklemek recomposition'ı sihirli biçimde engellemez. Ana mekanizma, `LazyColumn` içindeki composition state'in öğe kimliğine bağlanmasıdır; öğe başa eklendiğinde checkbox, focus veya animasyon state'inin yanlış satıra taşınmasını önler. `contentType` ise farklı satır şablonları bulunan akışlarda yeniden kullanılabilir composition havuzunu ayırır. Listede reklam, yükleme iskeleti ve normal kart varsa `contentType = { it.type }` kullanın; tüm satırlara aynı sabit content type vermek yanlış şablonun yeniden kullanılmasına neden olabilir.
Uzun ömürlü coroutine, `LaunchedEffect` veya listener içinde en güncel callback'i çağırmanız gerekiyorsa callback'i `remember` ile dondurmak yerine `rememberUpdatedState` kullanın. Bu araç skip optimizasyonu için değil, effect yeniden başlamadan güncel lambda'ya erişmek içindir.
@Composable
fun SessionObserver(onExpired: () -> Unit) {
val currentOnExpired by rememberUpdatedState(onExpired)
LaunchedEffect(Unit) {
sessionEvents.collect { event ->
if (event is SessionEvent.Expired) currentOnExpired()
}
}
}Android Studio eğitimi için Macrobenchmark ile önce-sonra kanıtı üretin
Compiler raporu potansiyel skip fırsatını gösterir, kullanıcı etkisini değil. Önce raporda unstable olan `FeedUiState` ve callback'leri tespit edin, sonra yalnızca bu değişiklikleri içeren bir commit ile Macrobenchmark çalıştırın. `FrameTimingMetric`, UI thread CPU süresi ile frame deadline aşımını ayrı raporladığından, yalnızca ortalama FPS'e bakmaktan daha faydalıdır. Aşağıdaki benchmark, warm başlangıçtan sonra aynı listeyi kaydırır.
@RunWith(AndroidJUnit4::class)
class FeedBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun scrollFeed() = benchmarkRule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(FrameTimingMetric()),
iterations = 10,
startupMode = StartupMode.WARM
) {
pressHome()
startActivityAndWait()
device.findObject(By.res("feed_list")).fling(Direction.DOWN)
}
}Compose test tag'inin `By.res("feed_list")` ile bulunması için root composable'a resource-id eşlemesini açın; aksi halde Macrobenchmark UIAutomator seçicisi elementi bulamaz. Bu özellikle yalnızca `Modifier.testTag()` ekleyip benchmark'ın cihazda neden başarısız olduğunu anlamaya çalışan ekiplerde sık görülür.
setContent {
Box(
Modifier.semantics { testTagsAsResourceId = true }
) {
FeedScreen(
modifier = Modifier.testTag("feed_list")
)
}
}Ölçümü release varyantında, fiziksel cihazda ve iki sürüm için aynı koşullarda yapın. İlk çalıştırmada baseline commit'in sonuç JSON'unu saklayın; ikinci çalıştırmada `frameDurationCpuMs` ile `frameOverrunMs` için P50 ve P90 değerlerini karşılaştırın. Örneğin P90 `frameOverrunMs` 8 ms'den 2 ms'ye düşerken CPU süresi değişmiyorsa, kazanç büyük olasılıkla skip edilen composition veya layout işinden gelmiştir. CPU süresi aynı kalıp overrun artıyorsa GC, render thread veya cihazdaki arka plan yükü gibi Compose dışı etkenleri Android Studio System Trace ile ayırın. Bu önce-sonra disiplini, mobil uygulama geliştirme eğitimi içinde rapor okumayı gerçek kullanıcı akıcılığıyla bağlayan kritik adımdır.
Android kursu projelerinde stability kontrolünü yayın hattına ekleyin
Bir android kursu veya kotlin kursu projesinde stability raporunu tek seferlik analiz yerine release kontrolü yapın. Pull request CI işinde `assembleRelease` sonrasında rapor dizinini artifact olarak yüklemek, review sırasında yeni eklenen unstable türlerin diff'ini görünür kılar. Örnek GitHub Actions adımı şudur:
- name: Build and collect Compose reports
run: ./gradlew :app:assembleRelease
- name: Upload Compose compiler reports
uses: actions/upload-artifact@v4
with:
name: compose-compiler-reports
path: app/build/compose_compiler/
if-no-files-found: errorBu kontrolü her unstable satırda build'i kıracak kadar katı başlatmayın. `Context`, `NavController`, `CoroutineScope` ve bazı UI adapter'ları doğası gereği unstable olabilir; bunları state modeline taşımak yerine event sınırında tutmak gerekir. Önce ekran bazında bir allowlist ve P90 frame budget belirleyin, ardından yeni bir liste ekranı bu bütçeyi aştığında benchmark sonucunu review şartı yapın. play store yayınlama öncesinde aynı release artifact üzerinde benchmark çalıştırmak da debug build'deki farklı optimizasyonların kararınızı yanıltmasını engeller.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Jetpack Compose stability raporu android eğitim projesinde nasıl açılır?
Kotlin Compose compiler eklentisi etkinleştirildikten sonra `composeCompiler { reportsDestination = ...; metricsDestination = ... }` tanımlayın ve `./gradlew :app:assembleDebug` çalıştırın. Çıktıdaki `composables.txt` dosyasında kritik satırlar için `skippable` ve `unstable` işaretlerini inceleyin; yalnızca IDE preview çıktısına güvenmeyin.
Kotlin eğitimi sırasında data class neden Jetpack Compose için her zaman stable değildir?
`data class` compiler'a alanların sonradan değişmeyeceğini garanti etmez. Örneğin `data class ScreenState(val items: MutableList
Android Studio eğitimi kapsamında Compose optimizasyonunu hangi araçla ölçmeliyim?
Önce Compose Compiler reports ile unstable parametreyi belirleyin, sonra release build üzerinde `MacrobenchmarkRule` ve `FrameTimingMetric` kullanın. Aynı cihazda en az 10 iterasyonla baseline ve değişiklik sürümünün P50/P90 `frameOverrunMs` değerlerini kıyaslayın. Sorun devam ederse Android Studio System Trace ile UI thread, RenderThread ve GC dilimlerini ayrı inceleyin.
Android MVVM mimarisi içinde NavController'ı UI state'e koymak doğru mu?
Hayır. `NavController` state modeli değildir ve stability raporunda unstable zincir oluşturabilir. ViewModel'den `SharedFlow
AI / LLM Discovery
Bu makale Opendart Akademi Android eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


