Jetpack Compose ekranlarında takılmayı JankStats ve Perfetto ile ölçün, kareyi kaçıran iş yükünü trace üzerinden bulun ve Android MVVM mimarisinde ana iş parçacığından ayırın.
Android Eğitimi: Jetpack Compose JankStats ile Kare Düşüşü Avı
Android eğitimi için doğru jank sinyalini kurmak
Bir kaydırma sorununun başlangıç metriği ortalama render süresi değil, kullanıcı etkileşimi sırasında oluşan jank oranı ve en kötü kareler olmalıdır. JankStats, platformun o cihazdaki yenileme hızına göre kare bütçesini değerlendirir; bu nedenle 120 Hz cihazda her kare için sabit 16.67 ms eşiği yazmaktan daha güvenilirdir. Uygulama modülüne AndroidX Metrics Performance bağımlılığını ekleyin ve her jank karesinde ekran adı, UI thread kare süresi ve uygulama sürümü gibi düşük kardinaliteli alanları kaydedin. URL, arama metni, kullanıcı kimliği veya ürün adı gibi kişisel ya da sınırsız değer üreten alanları telemetry etiketine koymayın.
class MainActivity : ComponentActivity() {
@Volatile
private var currentRoute = "unknown"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val jankStats = JankStats.createAndTrack(window) { frameData ->
if (frameData.isJank) {
telemetry.enqueue(
name = "ui_jank",
attributes = mapOf(
"route" to currentRoute,
"ui_duration_ms" to
(frameData.frameDurationUiNanos / 1_000_000.0).toString(),
"build_type" to BuildConfig.BUILD_TYPE
)
)
}
}
setContent {
val navController = rememberNavController()
LaunchedEffect(navController) {
navController.currentBackStackEntryFlow.collect { entry ->
currentRoute = entry.destination.route ?: "unnamed"
}
}
AppNavHost(navController)
}
}
}Bu android eğitim yaklaşımında amaç her tekil kareyi sunucuya göndermek değildir. Örneğin istemcide 30 saniyelik pencere içinde route bazında toplam kare, jank kare ve maksimum UI süresini biriktirip tek olay gönderin. Böylece ağ maliyeti sınırlanır ve bir ekranın jank oranını jankFrames / totalFrames olarak sürümler arasında karşılaştırabilirsiniz. JankStats callback'i ana iş parçacığına çok yakın çalıştığından callback içinde JSON serileştirme, disk yazma veya HTTP isteği yapmak bizzat ölçtüğünüz jank'i artıran yaygın bir hatadır.
Jetpack Compose jank'ini Perfetto ile nedenine bağlamak
JankStats size hangi rotada kötü kare oluştuğunu söyler, Perfetto ise o karede CPU'nun ne yaptığına cevap verir. Önce aynı cihazda aynı veri seti ve aynı kaydırma hareketiyle 10 tekrar alın. Her tekrarda jank oranı, p95 UI kare süresi ve en uzun kareyi kaydedin. Değişiklikten sonra aynı protokolü tekrarlayın; yalnızca tek bir 'iyi' trace ile karar vermeyin. Test öncesinde animasyon ölçeklerini kapatmak, giriş hareketinin daha tekrar edilebilir olmasını sağlar; işlem bitince geliştirici cihaz ayarlarını geri yükleyin.
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0
cat > jank_trace.pbtxt <<'EOF'
buffers { size_kb: 32768 fill_policy: RING_BUFFER }
data_sources {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "sched/sched_wakeup"
ftrace_events: "power/cpu_frequency"
}
}
}
data_sources { config { name: "android.surfaceflinger.frametimeline" } }
duration_ms: 15000
EOF
adb push jank_trace.pbtxt /data/local/tmp/
adb shell perfetto --txt -c /data/local/tmp/jank_trace.pbtxt -o /data/misc/perfetto-traces/feed-scroll.pftrace
adb pull /data/misc/perfetto-traces/feed-scroll.pftrace .Trace'i ui.perfetto.dev içinde açıp FrameTimeline seçin, kırmızı veya deadline'ı kaçırmış kareyi bulun ve aynı zaman aralığında main thread üzerindeki dilimleri inceleyin. Uzun bir Compose measure/layout dilimi, bitmap decode, Room sorgusu, Binder çağrısı ya da GC pause görmeniz farklı düzeltmeler gerektirir. Örneğin RenderThread boşken main thread'de 24 ms süren sıralama varsa GPU optimizasyonu etkisizdir; sorun doğrudan ana iş parçacığında çalışan CPU işidir. Bu ayrım, android studio eğitimi laboratuvarlarında Layout Inspector görüntüsünden daha kesin bir teşhis sağlar.
Android MVVM mimarisi içinde ana iş parçacığına sızan dönüşümleri ayırmak
Compose'da `collectAsStateWithLifecycle()` çağrısından önce ViewModel'in ürettiği listeyi filtrelemek veya sıralamak, her state yayımında ana iş parçacığını meşgul edebilir. Özellikle repository binlerce öğe yayıyorsa `sortedBy` için gereken O(n log n) iş, kaydırma ile aynı kare bütçesine girer. Aşağıdaki örnekte filtreleme ve sıralama `Dispatchers.Default` üzerinde yapılır; `mapLatest` ise kullanıcı yeni sorgu yazdığında tamamlanmamış eski dönüşümü iptal eder.
class FeedViewModel(
private val repository: ArticleRepository
) : ViewModel() {
private val query = MutableStateFlow("")
val uiState = combine(
query.debounce(150).distinctUntilChanged(),
repository.observeArticles()
) { text, articles -> text to articles }
.mapLatest { (text, articles) ->
withContext(Dispatchers.Default) {
val normalized = text.trim().lowercase()
val visible = articles.asSequence()
.filter { it.title.lowercase().contains(normalized) }
.sortedByDescending { it.publishedAt }
.toList()
FeedUiState.Content(visible)
}
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = FeedUiState.Loading
)
fun onQueryChanged(value: String) {
query.value = value
}
}Bu değişiklik sonrası Perfetto'da ana thread'deki sıralama dilimi kaybolmalı, fakat sonuç listesinin Compose'a her seferinde tamamen yeni bir `List` olarak gelmesi yine fazla kompozisyona neden olabilir. `LazyColumn` içinde kararlı anahtar kullanın ve satır modelini değişmez tutun: `items(items = articles, key = { it.id })`. Sadece `remember { mutableListOf() }` içine aynı nesneyi mutasyona uğratmak ise ters bir edge case'tir; Compose değişikliği gözlemlemeyebilir veya snapshot çatışması üretebilir. Kotlin eğitimi kapsamında bu örneğin asıl inceliği coroutine kullanmak değil, CPU ağırlıklı dönüşümün hangi dispatcher'da yürüdüğünü trace ile doğrulamaktır.
Mobil uygulama geliştirme eğitimi için ölçülebilir kabul kriteri
Bir android kursu ödevi ya da üretim PR'ı için kabul kriterini 'kaydırma daha akıcı görünüyor' diye bırakmayın. Örneğin Pixel sınıfı sabit bir test cihazında, aynı 3000 satırlık fixture ile 10 kaydırma turunda p95 UI kare süresinin 12 ms altında ve jank oranının yüzde 1'in altında kalmasını hedefleyin. Bu eşikler ürününüzün desteklediği cihaz yenileme hızına göre ayarlanmalıdır. Test fixture'ını ağdan değil yerel JSON veya Room prepopulate verisinden üretin; aksi halde DNS, TLS ve sunucu gecikmesi ölçüm varyansını büyütür.
Önceki ve sonraki sonuçları aynı tabloda saklayın: commit SHA, cihaz modeli, build variant, toplam kare, jank kare, p95 ve maksimum kare. Perfetto trace dosyasını CI artefact'ı olarak yüklemek, sayısal kötüleşme görüldüğünde incelemeyi mümkün kılar. Jetpack Compose için yalnızca release benzeri, minify edilmiş bir varyantı değerlendirin; debug derlemesindeki debug inspector ve doğrulamalar, gerçek kullanıcıdaki CPU dağılımını temsil etmez. Bu yöntem, kotlin kursu materyalinde coroutine kodunun gerçekten frame budget dışına çıktığını kanıtlayan pratik bir kontrol noktasıdır.
Play Store yayınlama öncesi jank regresyonunu yakalamak
Play Store yayınlama akışında yalnızca bundle'ın derlenmesi yeterli bir kalite kapısı değildir. Release AAB'yi önce cihazda bölünmüş APK'lara dönüştürerek hedef cihazda aynı ölçümü çalıştırın; bu, debug APK ile release dağıtımı arasındaki kaynak, shrinker ve ABI farklarını görünür kılar. `bundletool` çıktısını internal test kanalına göndermeden önce imzalama yapılandırmanızla doğrulayın.
bundletool build-apks --bundle app-release.aab --output app-release.apks --mode universal
unzip -p app-release.apks universal.apk > app-release-universal.apk
adb install -r app-release-universal.apkAndroid eğitim ve mobil uygulama geliştirme eğitimi programlarında bu süreci bir release checklist'e bağlamak yararlıdır: JankStats özetinde yeni route etiketi var mı, Perfetto'da ana thread üzerinde beklenmeyen disk veya Binder çağrısı var mı, release build'de aynı p95 korunuyor mu? Android MVVM mimarisi kullanılan projelerde ayrıca ViewModel'in UI state üretimi ile repository I/O'sunu ayrı trace bölümleriyle işaretleyin. Böylece Play Console tarafında bir kullanıcı sinyali oluştuğunda, hangi uygulama sürümü ve hangi ekran yolunun regresyona neden olduğunu kaynak düzeyinde daraltabilirsiniz.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
Android eğitimi sırasında JankStats mi yoksa Perfetto mu kullanmalıyım?
İkisini ardışık kullanın. JankStats ile route bazında jank oranını ve sorunlu sürümü bulun; aynı route için Perfetto FrameTimeline trace'i alın. JankStats semptomu, Perfetto ise o karedeki main thread, RenderThread, Binder ve scheduler nedenini verir.
Jetpack Compose ekranında p95 kare süresini nasıl karşılaştırmalıyım?
Aynı cihaz, aynı release benzeri build, aynı yerel fixture ve aynı kaydırma senaryosuyla en az 10 tekrar yapın. Her tekrarın UI kare sürelerini sıralayıp yüzde 95 konumundaki değeri alın. Değişiklik öncesi ve sonrası p95, toplam kare ve jank kare sayılarını birlikte raporlayın; tek maksimum kare sonuç için yeterli değildir.
Android MVVM mimarisi kullanırken filtreleme hangi dispatcher'da yapılmalı?
Room sorgusundan sonra yapılan büyük liste filtreleme, sıralama, JSON dönüştürme ve diff hazırlama işlemlerini ViewModel akışında `withContext(Dispatchers.Default)` ile CPU dispatcher'ına taşıyın. Ağ ve disk I/O için `Dispatchers.IO` kullanın. Kararın doğru olduğunu Perfetto'da main thread'den uzun CPU diliminin kalktığını görerek doğrulayın.
Android kursu projesinde play store yayınlama öncesi hangi jank testi yapılmalı?
Release AAB'den bundletool ile kurulabilir APK üretin, en yoğun LazyColumn veya pager ekranında sabit fixture ile tekrarlı kaydırma yapın ve 15 saniyelik Perfetto trace alın. JankStats özetini sürüm etiketiyle kaydedin; release sonucu önceki referansın p95 veya jank oranını aşıyorsa internal test kanalına geçmeden trace'i inceleyin.
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.


