JankStats Kitaplığı

JankStats kitaplığı, uygulamalarınızdaki performans sorunlarını izlemenize ve analiz etmenize yardımcı olur. Jank, oluşturulması çok uzun süren uygulama çerçevelerini, JankStats kitaplığı ise uygulamanızın jank istatistikleri hakkında raporlar sağlar.

İşlemler

JankStats, Android 7 (API düzeyi 24) ve sonraki sürümlerdeki FrameMetrics API'si veya önceki sürümlerde OnPreDrawListener gibi mevcut Android platform özelliklerini temel alır. Bu mekanizmalar, uygulamaların karelerin ne kadar sürede tamamlandığını izlemesine yardımcı olabilir. JanksStats kitaplığı, kullanımı daha dinamik ve kolay hale getiren iki ek özellik sunar: olumsuz bulgular ve kullanıcı arayüzü durumu.

Jank bulguları

Kare sürelerini takip etmek için FrameMetrics'i kullanabilirsiniz, ancak FrameMetrics gerçek olumsuzlukları belirleme konusunda herhangi bir yardım sağlamaz. Bununla birlikte JankStats, jank'ın ne zaman gerçekleştiğini belirlemek için yapılandırılabilir dahili mekanizmalara sahiptir. Böylece raporlar anında daha kullanışlı hale gelir.

Kullanıcı arayüzü durumu

Genellikle uygulamanızdaki performans sorunlarının bağlamını bilmek gerekir. Örneğin, FrameMetrics'i kullanan karmaşık, çok ekranlı bir uygulama geliştirirseniz ve uygulamanızın genellikle son derece kötü çerçevelere sahip olduğunu fark ederseniz, sorunun nerede ortaya çıktığını, kullanıcının ne yaptığını ve bunu nasıl tekrarladığını bilerek bu bilgileri bir bağlama oturtmak istersiniz.

JankStats, uygulama Etkinliği hakkında bilgi vermek için kitaplıkla iletişim kurmanızı sağlayan bir state API'sini kullanıma sunarak bu sorunu çözmektedir. JankStats kötü bir çerçeveyle ilgili bilgileri günlüğe kaydettiğinde jank raporlarına uygulamanın mevcut durumunu ekler.

Kullanım

JankStats'ı kullanmaya başlamak için her bir Window kitaplığını örneklendirin ve etkinleştirin. Her JankStats nesnesi, verileri yalnızca bir Window içindeki verileri izler. Kitaplığı örneklendirmek için bir Window örneğiyle birlikte bir OnFrameListener işleyicisi gerekir. Bu örneklerin ikisi de istemciye metrikleri göndermek için kullanılır. İşleyici her karede FrameData ile çağrılır ve şu ayrıntıları verir:

  • Kare başlangıç zamanı
  • Süre değerleri
  • Karenin duraklama olarak kabul edilip edilmeyeceği
  • Çerçeve sırasındaki uygulama durumuyla ilgili bilgileri içeren Dize çiftleri kümesi

JankStats'ı daha kullanışlı hale getirmek için uygulamaların kitaplığı FrameData'da raporlama için ilgili kullanıcı arayüzü durum bilgileriyle doldurması gerekir. Bu işlemi, tüm durum yönetimi mantığının ve API'lerin bulunduğu PerformanceMetricsState API üzerinden (doğrudan JankStats değil) yapabilirsiniz.

Başlatma

JankStats kitaplığını kullanmaya başlamak için önce JankStats bağımlılığını Gradle dosyanıza ekleyin:

implementation "androidx.metrics:metrics-performance:1.0.0-beta01"

Ardından, her bir Window için JankStats'ı başlatın ve etkinleştirin. Bir Etkinlik arka plana gittiğinde JankStats izlemesini de duraklatmanız gerekir. Etkinlik geçersiz kılma işlemlerinizde JankStats nesnesini oluşturun ve etkinleştirin:

class JankLoggingActivity : AppCompatActivity() {

    private lateinit var jankStats: JankStats


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        // metrics state holder can be retrieved regardless of JankStats initialization
        val metricsStateHolder = PerformanceMetricsState.getHolderForHierarchy(binding.root)

        // initialize JankStats for current window
        jankStats = JankStats.createAndTrack(window, jankFrameListener)

        // add activity name as state
        metricsStateHolder.state?.putState("Activity", javaClass.simpleName)
        // ...
    }

Yukarıdaki örnek, JankStats nesnesini oluşturduktan sonra geçerli Etkinlikle ilgili durum bilgilerini ekler. Bu JankStats nesnesi için ileride oluşturulacak tüm FrameData raporları artık Etkinlik bilgilerini de içermektedir.

JankStats.createAndTrack yöntemi, bir Window nesnesine referans alır. Bu, Window nesnesinin içindeki Görüntüleme hiyerarşisinin ve Window nesnesinin kendisi için proxy'dir. jankFrameListener, bu bilgileri platformdan JankStats'a dahili olarak iletmek için kullanılan iş parçacığında çağrılır.

Herhangi bir JankStats nesnesinde izleme ve raporlamayı etkinleştirmek için isTrackingEnabled = true çağrısı yapın. Varsayılan olarak etkin olsa da, bir etkinliğin duraklatılması izlemeyi devre dışı bırakır. Bu durumda, devam etmeden önce izlemeyi yeniden etkinleştirdiğinizden emin olun. İzlemeyi durdurmak için isTrackingEnabled = false numaralı telefonu arayın.

override fun onResume() {
    super.onResume()
    jankStats.isTrackingEnabled = true
}

override fun onPause() {
    super.onPause()
    jankStats.isTrackingEnabled = false
}

Raporlama

JankStats kitaplığı, her kare için etkin JankStats nesnelerine ait tüm veri izleme işlemlerinizi OnFrameListener hesabına raporlar. Uygulamalar bu verileri depolayabilir ve daha sonra yüklemek üzere toplayabilir. Daha fazla bilgi için Toplama bölümünde verilen örneklere göz atın.

Uygulamanızın kare başına raporları alması için OnFrameListener oluşturup sağlamanız gerekir. Bu dinleyici, uygulamalara devam eden olumsuzluk verileri sağlamak için her karede çağrılır.

private val jankFrameListener = JankStats.OnFrameListener { frameData ->
    // A real app could do something more interesting, like writing the info to local storage and later on report it.
    Log.v("JankStatsSample", frameData.toString())
}

İşleyici, FrameData nesnesiyle jank hakkında kare başına bilgiler sağlar. Bu dosya, istenen çerçeveyle ilgili aşağıdaki bilgileri içerir:

  • isjank: Çerçevede jank'ın olup olmadığını belirten boole işareti.
  • frameDurationUiNanos: Kare süresi (nanosaniye cinsinden).
  • frameStartNanos: Karenin başladığı zaman (nanosaniye cinsinden).
  • states: Kare sırasında uygulamanızın durumu.

Android 12 (API düzeyi 31) veya sonraki bir sürümü kullanıyorsanız kare süreleri hakkında daha fazla veri göstermek için aşağıdakileri kullanabilirsiniz:

Uygulama durumu ile ilgili bilgileri depolamak için işleyicide StateInfo kullanın.

OnFrameListener işlevinin, çerçeve başına bilgileri JankStats'a iletmek için dahili olarak kullanılan iş parçacığında çağrıldığını unutmayın. Android sürüm 6 (API düzeyi 23) ve önceki sürümlerde bu, ana (kullanıcı arayüzü) iş parçacığıdır. Android 7 (API düzeyi 24) ve sonraki sürümlerde FrameMetrics için oluşturulan ve kullanılan iş parçacığıdır. Her iki durumda da geri çağırmayı işlemek ve söz konusu iş parçacığındaki performans sorunlarını önlemek için hızlı bir şekilde geri dönmek önemlidir.

Ayrıca, geri çağırmada gönderilen FrameData nesnesinin, veri raporlama için yeni nesneler tahsis edilmesini önlemek amacıyla her çerçevede yeniden kullanıldığını unutmayın. Bu, geri çağırma geri arama geri döndüğünde söz konusu nesnenin eski ve eski olarak kabul edilmesi gerektiği için bu verileri başka bir yerde kopyalayıp önbelleğe almanız gerektiği anlamına gelir.

Toplama

Uygulama kodunuzun kare başına verileri toplamasını isteyebilirsiniz. Bu sayede, bilgileri kendi tercihinize göre kaydedip yükleyebilirsiniz. Kaydetme ve yükleme ile ilgili ayrıntılar alfa JankStats API sürümünün kapsamı dışında olsa da GitHub depomuzda bulunan JankAggregatorActivity öğesini kullanarak çerçeve başına verileri daha geniş bir koleksiyonda toplamak için ön Etkinlik'i görüntüleyebilirsiniz.

JankAggregatorActivity, yalnızca birçok çerçeveye yayılan bilgi koleksiyonunu raporlamak için daha yüksek düzeyde bir soyutlama sağlamak amacıyla kendi raporlama mekanizmasını JankStats OnFrameListener mekanizmasının üzerine eklemek için JankStatsAggregator sınıfını kullanır.

JankAggregatorActivity, doğrudan bir JankStats nesnesi oluşturmak yerine bir JankStatsAggregator nesnesi oluşturur. Bu nesne, dahili olarak kendi JankStats nesnesini oluşturur:

class JankAggregatorActivity : AppCompatActivity() {

    private lateinit var jankStatsAggregator: JankStatsAggregator


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        // Metrics state holder can be retrieved regardless of JankStats initialization.
        val metricsStateHolder = PerformanceMetricsState.getHolderForHierarchy(binding.root)

        // Initialize JankStats with an aggregator for the current window.
        jankStatsAggregator = JankStatsAggregator(window, jankReportListener)

        // Add the Activity name as state.
        metricsStateHolder.state?.putState("Activity", javaClass.simpleName)
    }

JankAggregatorActivity öğesinde izlemeyi duraklatmak ve devam ettirmek için benzer bir mekanizma kullanılır. Yaşam döngüsü değişiklikleri, uygulamada duraklama durumunu yakalamak için uygun bir zaman gibi görünür. Bu nedenle, issueJankReport() çağrısıyla rapor yayınlama sinyali olarak pause() etkinliği eklenir:

override fun onResume() {
    super.onResume()
    jankStatsAggregator.jankStats.isTrackingEnabled = true
}

override fun onPause() {
    super.onPause()
    // Before disabling tracking, issue the report with (optionally) specified reason.
    jankStatsAggregator.issueJankReport("Activity paused")
    jankStatsAggregator.jankStats.isTrackingEnabled = false
}

Yukarıdaki örnek kod, bir uygulamanın JankStats'ı etkinleştirmesi ve çerçeve verilerini alması için gereken tek şeydir.

Eyalet yönetimi

JankStats'ı özelleştirmek için diğer API'leri çağırmak isteyebilirsiniz. Örneğin, uygulama durumu bilgilerinin eklenmesi, olumsuzluğun meydana geldiği çerçeveler için bağlam sağlayarak çerçeve verilerini daha yararlı hale getirir.

Bu statik yöntem, belirli bir Görünüm hiyerarşisi için mevcut MetricsStateHolder nesnesini alır.

PerformanceMetricsState.getHolderForHierarchy(view: View): MetricsStateHolder

Etkin bir hiyerarşideki herhangi bir görünüm kullanılabilir. Dahili olarak bu işlem, bu görünüm hiyerarşisiyle ilişkili mevcut bir Holder nesnesi olup olmadığını kontrol eder. Bu bilgiler, söz konusu hiyerarşinin en üstündeki bir görünümde önbelleğe alınır. Böyle bir nesne yoksa getHolderForHierarchy() bir nesne oluşturur.

Statik getHolderForHierarchy() yöntemi, muhafaza eden örneği daha sonra almak üzere bir yerde önbelleğe almak zorunda kalmamanızı sağlar ve koddaki herhangi bir yerden mevcut bir durum nesnesini (hatta başka şekilde orijinal örneğe erişemeyecek olan kitaplık kodu) almanızı kolaylaştırır.

Döndürülen değerin durum nesnesinin kendisi değil, bir tutucu nesne olduğunu unutmayın. Tutucunun içindeki durum nesnesinin değeri yalnızca JankStats tarafından ayarlanır. Yani bir uygulama, bu görünüm hiyerarşisini içeren pencere için bir JankStats nesnesi oluşturursa durum nesnesi oluşturulur ve ayarlanır. Aksi takdirde, JankStats bilgileri izlemeden durum nesnesine ve uygulama veya kitaplık kodunun durumu eklemesi gerekmez.

Bu yaklaşım, JankStats'ın daha sonra doldurabileceği bir tutucu alınmasını sağlar. Harici kod, her zaman sahibi isteyebilir. Arayanlar, hafif Holder nesnesini önbelleğe alabilir ve dahili state mülkünün değerine bağlı olarak herhangi bir zamanda durumu ayarlamak için kullanabilir. Aşağıdaki örnek kodda olduğu gibi, durum yalnızca sahibinin dahili durum özelliği null olmadığında ayarlanır:

val metricsStateHolder = PerformanceMetricsState.getHolderForHierarchy(binding.root)
// ...
metricsStateHolder.state?.putState("Activity", javaClass.simpleName)

Uygulama, kullanıcı arayüzü/uygulama durumunu kontrol etmek için putState ve removeState yöntemleriyle durum ekleyebilir (veya kaldırabilir). JankStats bu çağrıların zaman damgasını günlüğe kaydeder. Bir çerçeve durumun başlangıç ve bitiş zamanıyla çakışırsa JeankStats, çerçevenin zamanlama verileriyle birlikte bu durumu bildirir.

Herhangi bir durum için iki bilgi ekleyin: key ("RecyclerView" gibi bir durum kategorisi) ve value ("kaydırma" gibi o sırada neler olduğuyla ilgili bilgiler).

Çerçeve verileriyle yanlış veya yanıltıcı bilgilerin raporlanmamasını sağlamak için bu durum artık geçerli olmadığında removeState() yöntemini kullanarak eyaletleri kaldırın.

putState(), daha önce eklenen bir key ile çağrılır ve bu durumdaki mevcut value değeri yenisiyle değiştirilir.

State API'nin putSingleFrameState() sürümü, bir sonraki raporlanan kareye yalnızca bir kez günlüğe kaydedilen bir durum ekler. Bunun ardından sistem, kodu otomatik olarak kaldırarak kodunuzda yanlışlıkla eski durumunun olmasını önler. JankStats tek kare durumlarını otomatik olarak kaldırdığı için removeState() ürününün singleFrame eşdeğeri yoktur.

private val scrollListener = object : RecyclerView.OnScrollListener() {
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        // check if JankStats is initialized and skip adding state if not
        val metricsState = metricsStateHolder?.state ?: return

        when (newState) {
            RecyclerView.SCROLL_STATE_DRAGGING -> {
                metricsState.putState("RecyclerView", "Dragging")
            }
            RecyclerView.SCROLL_STATE_SETTLING -> {
                metricsState.putState("RecyclerView", "Settling")
            }
            else -> {
                metricsState.removeState("RecyclerView")
            }
        }
    }
}

Eyaletler için kullanılan anahtarın, daha sonra analize olanak tanıyacak kadar anlamlı olması gerektiğini unutmayın. Özellikle daha önce eklenen bir key ile aynı duruma sahip bir durum önceki değerin yerini alacağından uygulamanızda veya kitaplığınızda farklı örnekleri olabilecek nesneler için benzersiz key adları kullanmaya çalışmalısınız. Örneğin, beş farklı RecyclerViews içeren bir uygulama, her biri için sadece RecyclerView kullanmak yerine bunların her biri için tanımlanabilir anahtarlar sağlamak isteyebilir ve sonuç olarak, kare verilerinin hangi örneğe karşılık geldiğini sonuç olarak kolayca ayırt edemeyebilir.

Jank bulguları

"jank" olarak kabul edilen unsurları belirlemek amacıyla dahili algoritmayı ayarlamak için jankHeuristicMultiplier özelliğini kullanın.

Sistem, jank'ı varsayılan olarak geçerli yenileme hızına göre iki kat daha uzun süren bir kare olarak tanımlar. Uygulamanın oluşturma süresiyle ilgili bilgiler tam olarak net olmadığından, olumsuzluk, yenileme hızını aşan bir değer olarak değerlendirilmez. Bu nedenle, tampon eklemek ve sorunları yalnızca gözle görülür performans sorunlarına yol açtığında bildirmek daha iyidir.

Bu değerlerin ikisi de bu yöntemlerle, uygulamanın durumuna daha yakından uygun olacak şekilde veya test için gerekli olduğu şekilde, duraklamanın gerçekleşmesini veya olmamasını sağlamaya yönelik testler uygulanarak değiştirilebilir.

Jetpack Compose'da kullanım

Şu anda Compose'da JankStats kullanmak için gereken çok az kurulum işlemi vardır. Yapılandırma değişikliklerinde PerformanceMetricsState kullanmaya devam etmek için aşağıdaki adımları uygulayın:

/**
 * Retrieve MetricsStateHolder from compose and remember until the current view changes.
 */
@Composable
fun rememberMetricsStateHolder(): PerformanceMetricsState.Holder {
    val view = LocalView.current
    return remember(view) { PerformanceMetricsState.getHolderForHierarchy(view) }
}

JankStats'ı kullanmak için mevcut durumu aşağıda gösterildiği gibi stateHolder öğesine ekleyin:

val metricsStateHolder = rememberMetricsStateHolder()

// Reporting scrolling state from compose should be done from side effect to prevent recomposition.
LaunchedEffect(metricsStateHolder, listState) {
    snapshotFlow { listState.isScrollInProgress }.collect { isScrolling ->
        if (isScrolling) {
            metricsStateHolder.state?.putState("LazyList", "Scrolling")
        } else {
            metricsStateHolder.state?.removeState("LazyList")
        }
    }
}

Jetpack Compose uygulamanızda JankStats'ı kullanma hakkında tüm ayrıntılar için performans örnek uygulamamıza göz atın.

Geri bildirim gönderme

Aşağıdaki kaynakları kullanarak geri bildirimlerinizi ve fikirlerinizi bizimle paylaşabilirsiniz:

Sorun izleyici
Hataları düzeltebilmemiz için sorunları bildirin.