Jetpack Compose listelerinde hissedilen takılmaları JankStats ve Macrobenchmark ile ölçün. Kare bütçesini aşan noktaları izole edip kod değişikliğinin P50, P90 ve jank oranına etkisini doğrulayın.
Android Eğitimi: JankStats ile Kaydırma Takılmalarını Ölçmek
Android eğitimi: JankStats ile jank sinyalini doğru toplamak
Kaydırma sorununu sadece kullanıcının 'takılıyor' geri bildirimiyle incelemek yerine, Activity penceresine JankStats bağlayın. JankStats, bir karenin platformun verdiği deadline'ı aşıp aşmadığını isJank olarak işaretler; sabit 16.67 ms eşiği kullanmaktan daha doğrudur. Çünkü yüksek yenileme hızlı cihazlarda kare bütçesi daha düşüktür. Bu android eğitimi yaklaşımında callback içinde ağ isteği, dosya yazımı veya ham log üretmeyin: callback ana UI iş akışına yakın çalışır ve kendisi yeni jank üretebilir.
class FeedActivity : ComponentActivity() {
private lateinit var jankStats: JankStats
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
jankStats = JankStats.createAndTrack(window) { frame ->
if (frame.isJank) {
JankCounters.record(
uiNanos = frame.frameDurationUiNanos,
cpuNanos = frame.frameDurationCpuNanos,
screen = frame.states["screen"]
)
}
}
jankStats.addState("screen", "feed")
setContent { FeedScreen() }
}
override fun onStop() {
jankStats.isTrackingEnabled = false
super.onStop()
}
override fun onStart() {
super.onStart()
jankStats.isTrackingEnabled = true
}
}Üretim telemetrisi için her jank karesini olay olarak göndermek yerine, ekran oturumu başına jankFrameCount, totalFrameCount ve en yüksek 10 frameDurationUiNanos değerini bellekte toplayıp ekran kapanışında tek payload gönderin. addState() değerlerini sınırlı bir kümede tutun: ekran adı, liste tipi veya deney kolu uygundur; ürün kimliği ya da her satırın ID'si uygun değildir. Benzersiz state değerleri, analiz tarafında yüksek cardinality üretir ve hangi kod yolunun sorumlu olduğunu bulanıklaştırır.
Jetpack Compose kaydırmasını Macrobenchmark ile tekrar üretmek
JankStats sorunun sahada var olduğunu gösterir, ancak düzeltmeyi doğrulamak için aynı hareketi kontrollü koşulda tekrar etmeniz gerekir. Ayrı bir Macrobenchmark modülünde FrameTimingMetric() ile cold start sonrası aynı listeyi fling edin. Ölçümü debug APK ile yapmayın; debug derleme, debugger kontrolleri ve farklı optimizasyonlar nedeniyle release davranışını temsil etmez. Ölçümden önce cihazı şarja bağlayın, termal durumu sabitleyin ve en az 10 iterasyon çalıştırın.
@RunWith(AndroidJUnit4::class)
class FeedScrollBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun scrollFeed() = benchmarkRule.measureRepeated(
packageName = "com.opendart.feed",
metrics = listOf(FrameTimingMetric()),
iterations = 10,
startupMode = StartupMode.COLD,
setupBlock = {
pressHome()
startActivityAndWait()
}
) {
device.findObject(By.res("feed_list"))
.fling(Direction.DOWN)
}
}Compose düğümünü UiAutomator ile kararlı bulmak için kök semantics düğümünde testTagsAsResourceId = true, listede de Modifier.testTag("feed_list") kullanın. Benchmark çıktısındaki JSON dosyasından aynı cihaz ve aynı build varyantı için önce-sonra karşılaştırması yapın: P50 genellikle sürekli maliyeti, P90 ise görünür takılma kümelerini açığa çıkarır. Örneğin değişiklik öncesi ve sonrası P90 frame süresini, jank yüzdesini ve iterasyonlar arası saçılmayı birlikte kaydedin; yalnızca tek iyi koşuyu seçmek regresyon gizler.
Kotlin eğitimi: LazyColumn'da ölçüm ve item kimliği maliyeti
Jetpack Compose içindeki LazyColumn'da sık görülen hata, her veri güncellemesinde kimliksiz öğeler üretmek ve satır türlerini Compose'a bildirmemektir. key, öğenin kompozisyon kimliğini korur; contentType ise aynı tipteki satırların ölçüm ve yerleşim altyapısının yeniden kullanımına yardım eder. Özellikle reklam, başlık ve içerik kartlarının karıştığı feed'lerde bu iki bilgi olmadan kaydırma sırasında daha fazla slot yeniden kurulabilir.
@Composable
fun FeedList(items: ImmutableList) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.testTag("feed_list")
) {
items(
items = items,
key = { row -> row.id },
contentType = { row -> row::class.simpleName }
) { row ->
when (row) {
is FeedRow.Article -> ArticleCard(row)
is FeedRow.Header -> SectionHeader(row)
is FeedRow.Ad -> AdCard(row)
}
}
}
} Bu kotlin eğitimi örneğinde kritik ayrıntı şudur: key = { it.hashCode() } kullanmayın. Mutable alanlardan üretilen hash değişirse Compose bunu eski satırın silinip yenisinin eklenmesi gibi görür; scroll konumu, item içi remember durumu ve animasyonlar beklenmedik biçimde sıçrayabilir. Ayrıca tarih biçimleme, Markdown parse etme veya bitmap decode işlemini item composable gövdesinde yapmayın. Android Studio Profiler CPU kaydında bu fonksiyonları kaydırma ile aynı zaman aralığında görün; sonra sonucu repository katmanında önbelleğe alıp Macrobenchmark P90 değerini yeniden ölçün.
Android MVVM mimarisi ile UI akışındaki gereksiz yayınları sınırlamak
android mvvm mimarisi içinde repository, websocket veya Room kaynağı kısa sürede çok sayıda ara durum üretiyorsa ViewModel her emisyon için yeni bir liste eşleyip Compose'a verebilir. Görsel olarak yalnızca son durum anlamlıysa, UI state akışında conflate() kullanmak yavaş ana toplayıcının eski ara snapshot'ları atlamasını sağlar. Bunu ödeme, navigation, hata bildirimi gibi kaybolmaması gereken event'lerde kullanmayın; yalnızca tam snapshot niteliğindeki ekran durumunda kullanın.
class FeedViewModel(
repository: FeedRepository
) : ViewModel() {
val uiState: StateFlow = repository.observeFeed()
.conflate()
.map { entities ->
FeedUiState.Content(
items = entities.map { it.toFeedRow() }.toPersistentList()
)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = FeedUiState.Loading
)
} Önce Android Studio Profiler'da CPU kaydı alıp toFeedRow() ve liste tahsislerinin fling sırasında kaç kez çağrıldığını sayın. Ardından yukarıdaki değişiklikten sonra aynı Macrobenchmark senaryosunu çalıştırın. WhileSubscribed(5_000) yapılandırması ekranın kısa süreli durup geri gelmesinde upstream'i hemen kapatmaz; ancak repository akışı pahalı bir soket veya sorgu açıyorsa bu 5 saniyelik maliyetin ürün gereksinimiyle uyumlu olduğundan emin olun. Süreyi rastgele artırmak yerine profiler ve ağ metrikleriyle karar verin.
Android Studio eğitimi: CI eşiği ve play store yayınlama öncesi kontrol
Bir android studio eğitimi laboratuvarında elde edilen benchmark sonucu, CI'da otomatik reddedilmediği sürece zamanla unutulur. Bağlı fiziksel test cihazında Macrobenchmark çıktısını artifact olarak saklayın ve referans build'e göre P90 frame süresi veya jank oranı belirlediğiniz toleransı aşarsa merge isteğini başarısız yapın. Örneğin feed senaryosunda P90'ın referansın yüzde 10 üstüne çıkması inceleme gerektirebilir; sabit bir milisaniye eşiği cihaz yenileme hızı değiştiğinde yanıltıcıdır.
play store yayınlama öncesinde internal test kanalından alınan gerçek cihaz telemetrisi ile laboratuvar benchmark'ını ayrı grafiklerde tutun. Laboratuvar tekrarlanabilir hareketi ölçer, saha ise düşük bellek, farklı ekran yoğunluğu ve gerçek veri boyutunu ortaya çıkarır. Bir mobil uygulama geliştirme eğitimi kapsamında ekip için pratik kontrol listesi şudur: sürüm adayında aynı benchmark commit'ini çalıştırın, JankStats state'lerinde yeni yüksek cardinality olmadığını doğrulayın ve yalnızca anlamlı P90/jank regresyonlarında Perfetto trace alın. Bu akış, android kursu veya kotlin kursu projelerinde 'hissettim' yerine tekrar üretilebilir kanıtla karar verilmesini sağlar.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Android eğitimi kapsamında JankStats ile kaç ms üzerini jank kabul etmeliyim?
Sabit 16 ms veya 33 ms eşiğini birincil kural yapmayın. JankStats'in frame deadline tabanlı isJank işaretini toplayın; ardından Macrobenchmark FrameTimingMetric ile aynı cihazdaki P50, P90 ve jank oranını önce-sonra kıyaslayın. Bu, 60 Hz ve daha yüksek yenileme hızlı cihazlarda farklı kare bütçelerini doğru ele alır.
Jetpack Compose LazyColumn için android studio eğitimi sırasında hangi profiler kullanılmalı?
Önce Android Studio Profiler CPU kaydında fling zaman aralığını seçin ve item composable, image decode, tarih formatlama ve liste map işlemlerini arayın. Sonra Macrobenchmark ile sayısal frame dağılımını alın. Çağrı ağacı tek başına kullanıcı etkisini vermez; benchmark tek başına da suçlu fonksiyonu göstermez.
Android MVVM mimarisi içinde Flow conflate kullanmak veri kaybettirir mi?
Evet, yavaş collector çalışırken ara emisyonlar atlanabilir. Bu nedenle sadece tam ekran snapshot'larında kullanın; örneğin canlı fiyat listesinin kullanıcıya çizilecek son hali. Sipariş oluşturma sonucu, navigation komutu veya analitik teslim garantisi gereken event'leri SharedFlow veya kalıcı kuyrukla ayrı taşıyın.
Kotlin eğitimi projelerinde Macrobenchmark sonucu neden her çalıştırmada farklı çıkar?
Termal throttling, şarj durumu, arka plan süreçleri, başlangıç modu ve veri seti fark yaratır. Test cihazını sabitleyin, aynı release APK'yı kullanın, cold veya warm startup modunu açıkça seçin, 10 veya daha fazla iterasyon çalıştırın ve tek ölçüm yerine dağılımı karşılaştırın.
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.



