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:
FrameDataApi24
,frameDurationCpuNanos
sayesinde karenin GPU olmayan kısımlarında geçirilen sürenin görüntülenmesini sağlar.FrameDataApi31
,frameOverrunNanos
aracılığıyla karenin tamamlanması için geçen kare bitiş süresinden sonra geçen süreyi gösterir.
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.
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken gösterilir
- Temel Profiller oluşturma {:#creator-profile-rules}
- Mikro Karşılaştırma Enstrümantasyonu Bağımsız Değişkenleri
- Makrobenchmark Enstrümantasyon Bağımsız Değişkenleri