• 28.08.2026 02:36:05
  • Admin Admin

Jetpack Compose kullanan uygulamalarda Baseline Profile üretimini Macrobenchmark ile ölçülebilir hale getirin. Bu android eğitimi, soğuk başlangıçta JIT derleme ve dexopt maliyetini ayırmayı gösterir.

Jetpack Compose'da Baseline Profile ile İlk Açılışı Ölçerek Hızlandırmak

Jetpack Compose ve Baseline Profile: Ölçmeden Profil Yazmayın

Baseline Profile, uygulamanın ilk kritik akışlarında çalışacak sınıf ve metotlar için ART'a önceden derleme ipucu verir. Soğuk başlangıçta farkın mekanizması şudur: profil yokken ART, sık kullanılan dex kodunu çalıştırırken yorumlama ve JIT derleme maliyeti öder; profil yüklüyken bu kodun bir bölümü uygulama kurulumu veya arka plan optimizasyonu sırasında AOT derlenebilir. Bu nedenle sadece debug APK'yi Android Studio'dan çalıştırarak süre ölçmek yanıltıcıdır: debug build'in debuggable bayrağı, farklı optimizasyonları ve IDE bağlantısı gerçek dağıtım davranışını değiştirir.

Bu android eğitim içinde ilk referans ölçümünü release benzeri, minify kapalı veya açık seçiminiz neyse üretimle aynı varyantta alın. `adb shell am force-stop` her tekrarda süreç belleğini temizler; `am start -W` çıktısındaki `TotalTime` hızlı bir ön kontrol sunar. En az 20 tekrarın medyanını kaydedin; tek bir ölçüm, CPU frekansı ve arka plan işlerinden etkilenir.

./gradlew :app:assembleRelease
adb install -r app/build/outputs/apk/release/app-release.apk

for i in $(seq 1 20); do
  adb shell am force-stop com.example.catalog
  adb shell am start -W -n com.example.catalog/.MainActivity | grep -E 'TotalTime|ThisTime'
done

Bu komut yalnızca karşılaştırma için ilk sinyaldir. `TotalTime`, Activity görünür olana kadarki framework süresidir; Compose ekranındaki ilk veri yüklemesi, animasyon veya sonraki frame jank'ini tek başına açıklamaz. Bu ayrımı yapmak için sonraki bölümde Macrobenchmark kullanın.

android studio eğitimi akışında Macrobenchmark modülünü kurmak

Ayrı bir `benchmark` Android library modülü oluşturun ve test APK'sinin hedef uygulamayı dışarıdan başlatmasını sağlayın. Macrobenchmark testleri emülatörde çalışabilir, fakat CI'da sabit API seviyesi, ekran çözünürlüğü ve mümkünse fiziksel cihaz kullanmak varyansı azaltır. Ölçülen uygulamanın `profileable` olması gerekir; release manifest'ine bunu koşullu eklemek, üretimde debugger açmadan Perfetto ve benchmark erişimi sağlar.

<!-- app/src/release/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:profileable="true" />
</manifest>

// benchmark/build.gradle.kts
dependencies {
    androidTestImplementation("androidx.benchmark:benchmark-macro-junit4:<version>")
    androidTestImplementation("androidx.test.uiautomator:uiautomator:<version>")
}

android {
    defaultConfig {
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }
    targetProjectPath = ":app"
}

Önce yalnızca başlangıcı ölçen testi yazın. `CompilationMode.None()` profil etkisini kapatır; bu test, profil eklenmeden önceki karşılaştırma tabanıdır. `StartupMode.COLD`, uygulama sürecini öldürerek diskten sınıf yükleme, kaynak çözümleme ve Compose composition maliyetini tekrar devreye sokar.

@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun coldStart_withoutProfile() = benchmarkRule.measureRepeated(
        packageName = "com.example.catalog",
        metrics = listOf(StartupTimingMetric()),
        compilationMode = CompilationMode.None(),
        startupMode = StartupMode.COLD,
        iterations = 15
    ) {
        pressHome()
        startActivityAndWait()
    }
}

Bir android kursu veya mobil uygulama geliştirme eğitimi projesinde yaygın hata, benchmark hedefinin debug varyantını ölçmektir. Debug APK'de loglama, doğrulamalar ve farklı optimizasyon kararları bulunduğundan sonuçlar Play Store'a gönderilecek artifact'i temsil etmez. Testin hedef paket adı ve build type'ı CI'da açıkça doğrulanmalıdır.

kotlin eğitimi için gerçek kullanıcı akışından profil üretimi

Profil üreticisi, uygulamanın yalnızca launcher Activity'sini açmamalıdır. Kullanıcı ilk açılışta giriş yapıyor, izin diyaloğunu geçiyor veya ana listeyi kaydırıyorsa, bu akışta yüklenen Compose composable'ları, navigation graph sınıfları ve serialization kodu da profile dahil edilmelidir. Baseline Profile modülünde `collect` bloğu, ölçüm testi değil profil kuralı üretir; akışı deterministik tutmak için ağ yerine fake backend, sabit seed ve test hesabı kullanın.

@RunWith(AndroidJUnit4::class)
class CatalogBaselineProfile {
    @get:Rule
    val baselineProfileRule = BaselineProfileRule()

    @Test
    fun generate() = baselineProfileRule.collect(
        packageName = "com.example.catalog"
    ) {
        startActivityAndWait()
        device.wait(Until.hasObject(By.res("catalog_list")), 5_000)
        device.findObject(By.res("catalog_list")).fling(Direction.DOWN)
        device.findObject(By.text("Details")).click()
        device.waitForIdle()
    }
}

Buradaki incelik, `waitForIdle()` çağrısının sonsuz animasyon, sürekli Flow emisyonu veya shimmer placeholder kullanan ekranda uzun bekleyebilmesidir. Bu durumda UI Automator beklemesini kararlı bir `testTag` veya kaynak kimliğiyle sınırlandırın. Örneğin Compose tarafında `Modifier.testTag("catalog_list")` ekleyip, semantics merge nedeniyle görünmeyen bir alt düğüm yerine listenin kendisini seçin.

kotlin kursu materyallerinde sık görülen başka hata, ilk açılışta uzak konfigürasyon sonucuna göre tamamen farklı bir navigation dalı açmaktır. Aynı profile hem login hem home akışını rastgele sokmak, üretilen kuralları şişirebilir. Akışları ayrı testlerde tanımlayın ve kritik yolun gerçekten profile girdiğini oluşturulan `baseline-prof.txt` dosyasını code review sırasında inceleyin.

android mvvm mimarisi içinde başlangıç maliyetini ayırmak

android mvvm mimarisi kullanan bir Compose uygulamasında ilk frame gecikmesi çoğu zaman ViewModel oluşturma zincirinden gelir: Hilt graph kurulumu, repository'nin DataStore okuması ve ilk `StateFlow` emisyonu aynı kritik yola girebilir. Baseline Profile bu kodu daha hızlı çalıştırabilir, ancak ana thread üzerinde bloklayan I/O'yu ortadan kaldırmaz. `runBlocking` ile DataStore veya Room okuyan bir başlangıç kodu varsa önce onu kaldırın; aksi halde profile sonrası medyan iyileşse bile yavaş disk veya şifre çözme anlarında tail latency kalır.

class HomeViewModel(
    private val settings: SettingsRepository
) : ViewModel() {
    val uiState: StateFlow<HomeUiState> = settings.observe()
        .map { HomeUiState.Content(it) }
        .stateIn(
            viewModelScope,
            SharingStarted.WhileSubscribed(5_000),
            HomeUiState.Loading
        )
}

// Yanlış: Composition veya Activity onCreate icinde runBlocking { dataStore.data.first() }
// Dogru: ilk frame Loading ile cizilir, veri Flow uzerinden asenkron akar.

Neden bu değişiklik önemlidir: `runBlocking` ana thread'i disk okuması tamamlanana kadar park eder ve Choreographer'ın ilk vsync öncesi frame üretmesini engeller. Asenkron `StateFlow` yaklaşımında ilk composition `Loading` durumunu çizer; veri geldiğinde ikinci composition gerçekleşir. Perfetto'da `androidx.tracing.trace` ile repository başlangıcını işaretleyin ve `sched`, `freq`, `binder_driver` ile ana thread'in gerçekten runnable mı yoksa I/O bekliyor mu olduğunu kontrol edin.

fun loadRemoteFlags(): Flags = trace("remote_flags_load") {
    flagsClient.fetchBlocking()
}

Önce-sonra doğrulaması ve play store yayınlama öncesi kontrol

Aynı Macrobenchmark testini önce `CompilationMode.None()`, sonra profile paketlenmiş artifact ile `CompilationMode.Partial(BaselineProfileMode.Require)` altında çalıştırın. `Require` seçeneği, profil kurulmamışsa testi başarısız yapar; bu, Gradle yapılandırması bozulduğunda yanlışlıkla profilsiz sürümü başarılı saymayı engeller. Sonuç JSON'larını CI artifact'i olarak saklayın ve medyan ile p95 değerlerini karşılaştırın.

@Test
fun coldStart_withBaselineProfile() = benchmarkRule.measureRepeated(
    packageName = "com.example.catalog",
    metrics = listOf(StartupTimingMetric()),
    compilationMode = CompilationMode.Partial(
        baselineProfileMode = BaselineProfileMode.Require
    ),
    startupMode = StartupMode.COLD,
    iterations = 15
) {
    pressHome()
    startActivityAndWait()
}

Karşılaştırmayı geçerli kılmak için iki koşuda da aynı cihaz imajını, aynı uygulama verisi durumunu ve aynı başlangıç rotasını kullanın. Profil üretiminden sonra örneğin medyan `timeToInitialDisplayMs` 620 ms'den 470 ms'ye inerken p95 1100 ms'de kalıyorsa, yalnızca profile başarısını ilan etmeyin; Perfetto trace'inde p95 koşularındaki ağ, keystore veya GC olayını ayrı inceleyin. Baseline Profile deterministik kod yükleme maliyetini hedefler, değişken uzak servis gecikmesini değil.

play store yayınlama aşamasında bundle içindeki profilin gerçekten dağıtıldığını release pipeline'da doğrulayın. Yerel APK'ye profil eklenmiş olması yeterli değildir; imzalı app bundle, flavor ve dynamic feature kombinasyonu farklı olabilir. Internal testing kanalından kurulan pakette `adb shell dumpsys package com.example.catalog` ve benchmark'ın `BaselineProfileMode.Require` sonucu ile son artifact'i kontrol edin. Bu kontrol, android eğitimi kapsamında öğrenilen ölçümün yalnızca geliştirme makinesinde kalmamasını sağlar.

Sık Sorulan Sorular

Jetpack Compose projesinde Baseline Profile hangi akışlar için üretilmeli?

Launcher açılışı, ilk anlamlı liste veya dashboard, kullanıcıların ilk oturumda sık kullandığı detay ekranı ve kritik scroll akışını ayrı `BaselineProfileRule.collect` testleriyle kapsayın. Ağ sonucuna bağlı rotaları fake backend ile sabitleyin; aksi halde her üretimde farklı sınıflar profile girebilir.

android studio eğitimi sırasında neden debug APK ile startup benchmark yapılmamalı?

Debug varyantı `debuggable` davranışı, ek loglama ve üretimden farklı shrink/optimizasyon kararları nedeniyle release artifact ile aynı sınıf yükleme maliyetini vermez. Macrobenchmark hedefini release benzeri varyanta bağlayın, manifest'te `android:profileable="true"` kullanın ve ölçümde `StartupMode.COLD` seçin.

kotlin eğitimi örneklerinde Baseline Profile runBlocking sorununu çözer mi?

Hayır. Profil, önceden bilinen sıcak kodu derleme açısından hızlandırır; ana thread üzerindeki `runBlocking { dataStore.data.first() }` disk ve IPC beklemesini sürdürür. Okumayı ViewModel'de Flow olarak yayınlayın, ilk frame'i Loading state ile çizin ve Perfetto'da ana thread wait state'lerini doğrulayın.

play store yayınlama öncesinde Baseline Profile paketlendiği nasıl doğrulanır?

Internal testing kanalından kurulan son imzalı artifact üzerinde `CompilationMode.Partial(BaselineProfileMode.Require)` ile Macrobenchmark çalıştırın. Test başarısızsa profil kurulum zinciri yoktur; başarılıysa aynı cihazda profilsiz `CompilationMode.None()` sonucu ile medyan ve p95 başlangıç sürelerini 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.

Opendart Akademi llms.txt