Biblioteka JankStats pomaga śledzić i analizować problemy z wydajnością aplikacji. Jank odnosi się do klatek aplikacji, których renderowanie trwa zbyt długo, a biblioteka JankStats zawiera raporty o statystykach zaciętych aplikacji.
Uprawnienia
JankStats rozszerza możliwości istniejącej platformy Androida, Interfejs FrameMetrics API na Androidzie 7 (poziom interfejsu API 24) i nowszych lub OnPreDrawListener wcześniej wersji. Mechanizmy te mogą pomóc aplikacjom śledzić, ile czasu zajmuje gotowe. Biblioteka JanksStats oferuje dwie dodatkowe możliwości, dzięki którym bardziej dynamiczny i łatwiejszy w użyciu: błędna heurystyka i stan interfejsu.
Heurystyka stymulująca
Możesz wykorzystać FrameMetrics do śledzenia czasu trwania klatek, ale FrameMetrics nie nie pomoże Ci w znalezieniu problemu. JankStats ma natomiast które pozwalają określić moment zacinania się aplikacji. raporty są bardziej przydatne.
Stan interfejsu
Często dobrze jest poznać kontekst problemów z wydajnością aplikacji. Jeśli np. tworzysz złożoną aplikację na wiele urządzeń, która korzysta z FrameMetrics i stwierdzisz, że Twoja aplikacja często ma wyjątkowo niezdrowe ramki, umieścić te informacje w kontekście, wiedząc, gdzie wystąpił problem, co robi użytkownik i jak to powieli.
JankStats rozwiązuje ten problem, wprowadzając interfejs API state
, który pozwala
komunikuje się z biblioteką, aby udostępniać informacje o aktywności w aplikacji; Kiedy
JankStats rejestruje informacje o nieprawidłowej ramce, w tym bieżący stan
aplikacji w raportach o bałaganach.
Wykorzystanie
Aby zacząć korzystać z JankStats, utwórz i włącz bibliotekę dla każdego
Window
Każdy obiekt JankStats śledzi dane
tylko w obrębie Window
. Utworzenie instancji biblioteki wymaga instancji Window
wraz z OnFrameListener
służą do wysyłania wskaźników do klienta. Wywołanie funkcji detektora przy użyciu parametru
FrameData
w każdej klatce
oraz szczegóły:
- Czas rozpoczęcia renderowania
- Czas trwania
- Określa, czy ramka ma być uznawana za zacinającą się.
- Zbiór par ciągu znaków zawierający informacje o stanie aplikacji w trakcie klatki
Aby statystyki Jank były bardziej przydatne, aplikacje powinny umieszczać w bibliotece dane
istotne informacje o stanie UI na potrzeby raportowania w FrameData. Możesz to zrobić
przez
PerformanceMetricsState
API (nie bezpośrednio JankStats), gdzie cała logika zarządzania stanami oraz interfejsy API
na żywo.
Inicjalizacja
Aby zacząć korzystać z biblioteki JankStats, najpierw dodaj zależność JankStats do Plik Gradle:
implementation "androidx.metrics:metrics-performance:1.0.0-beta01"
Następnie zainicjuj i włącz JankStats dla każdego elementu Window
. Warto też wstrzymać
Śledzenie JankStats, gdy aktywność rozpoczyna się w tle. Utwórz i włącz
obiekt JankStats w sekcji Aktywność zastępuje:
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)
// ...
}
W przykładzie powyżej wstrzyknięto informacje o stanie bieżącego Działanie po utworzeniu obiektu JankStats. Wszystkie przyszłe raporty FrameData utworzona dla tego obiektu JankStats zawiera teraz również informacje o aktywności.
Metoda JankStats.createAndTrack
odwołuje się do Window
który jest serwerem proxy hierarchii widoków wewnątrz Window
, a także
dla samej Window
. Funkcja jankFrameListener
jest wywoływana w tym samym wątku, który jest używany
aby przekazywać te informacje z platformy do narzędzia JankStats wewnętrznie.
Aby włączyć śledzenie i raportowanie dowolnego obiektu JankStats,
Zadzwoń pod numer isTrackingEnabled = true
. Mimo że ta opcja jest domyślnie włączona,
wstrzymanie aktywności wyłącza śledzenie. W takim przypadku ponownie włącz
śledzenie, zanim przejdziesz dalej. Aby wyłączyć śledzenie, zadzwoń pod numer isTrackingEnabled = false
.
override fun onResume() {
super.onResume()
jankStats.isTrackingEnabled = true
}
override fun onPause() {
super.onPause()
jankStats.isTrackingEnabled = false
}
Raportowanie
Biblioteka JankStats przekazuje wszystkie dane śledzenia, dla każdej klatki,
OnFrameListener
dla włączonych obiektów JankStats. Aplikacje mogą przechowywać i agregować te dane
w celu późniejszego przesłania. Aby uzyskać więcej informacji,
zapoznaj się z przykładami podanymi w sekcji Agregacja.
Aby aplikacja otrzymała dostęp do usługi, musisz utworzyć i podać atrybut OnFrameListener
w raportach dla poszczególnych klatek. Ten detektor jest wywoływany w każdej klatce, aby zapewnić stały ruch
zapchać dane aplikacjom.
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())
}
Detektor udostępnia informacje o zakłóceniu w przypadku poszczególnych klatek za pomocą funkcji
FrameData
. Ten
zawiera następujące informacje o żądanej ramce:
isjank
: Flaga wartości logicznej, która wskazuje, czy w ramce wystąpił problem.frameDurationUiNanos
: Czas trwania klatki (w nanosekundach).frameStartNanos
: Czas rozpoczęcia klatki (w nanosekundach).states
: Stan aplikacji w klatce.
Jeśli masz Androida 12 (poziom interfejsu API 31) lub nowszego, możesz użyj tego tagu, aby uzyskać więcej informacji o czasie trwania klatek:
FrameDataApi24
zapewniaframeDurationCpuNanos
aby wyświetlić czas w obszarach klatki bez procesora graficznego.FrameDataApi31
zapewniaframeOverrunNanos
aby wyświetlić czas, jaki upłynął od momentu renderowania klatki. gotowe.
Użyj StateInfo
w
detektor do przechowywania informacji o stanie aplikacji.
Zwróć uwagę, że funkcja OnFrameListener
jest wywoływana w tym samym wątku używanym wewnętrznie do
dostarcza JankStats informacje dotyczące klatek.
W Androidzie w wersji 6 (poziom interfejsu API 23) i starszych jest to wątek główny.
W Androidzie w wersji 7 (poziom interfejsu API 24) i nowszych
utworzony i używany przez FrameMetrics. W obu przypadkach ważne jest, aby
obsługuje wywołanie zwrotne i umożliwia szybkie zwracanie, co pozwala uniknąć problemów z wydajnością
w tym wątku.
Pamiętaj też, że obiekt FrameData wysłany w wywołaniu zwrotnym jest ponownie używany w każdym aby uniknąć przydzielania nowych obiektów do raportowania danych. Oznacza to, że musisz skopiować i zapisać je w pamięci podręcznej w innym miejscu, ponieważ obiekt ten powinien być uważane za przestarzałe i przestarzałe, gdy tylko wywołanie zwrotne zostanie zwrócone.
Agreguję
Prawdopodobnie kod aplikacji gromadzi dane poszczególnych klatek,
o zapisywaniu i przesłaniu informacji według własnego uznania. Chociaż szczegóły
dotyczące zapisywania i przesyłania wykraczają poza zakres alfa interfejsu JankStats API.
możesz wyświetlić wstępne działanie związane z agregacją danych dla poszczególnych klatek.
do większej kolekcji przy użyciu usługi JankAggregatorActivity
dostępnej w
Repozytorium GitHub.
JankAggregatorActivity
używa klasy JankStatsAggregator
do nałożenia własnych raportów
oparty na mechanizmie JankStats OnFrameListener
, aby zapewnić
wyższego poziomu abstrakcji w przypadku raportowania tylko zbioru informacji, które
obejmuje wiele klatek.
Zamiast bezpośrednio tworzyć obiekt JankStats, JankAggregatorActivity
tworzy JankStatsAggregator,
który tworzy wewnętrznie własny obiekt JankStats:
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)
}
Podobny mechanizm jest stosowany w JankAggregatorActivity
do wstrzymywania i
wznowić śledzenie, dodając zdarzenie pause()
jako sygnał do problemu
raport z wywołaniem issueJankReport()
, ponieważ zmiany w cyklu życia
odpowiedni czas na przechwycenie stanu zacięć w aplikacji:
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
}
Przykładowy kod powyżej to wszystko, czego aplikacja potrzebuje, aby włączyć JankStats i otrzymywać danych ramki.
Zarządzaj stanem
Możesz też chcieć wywołać inne interfejsy API, aby dostosować JankStats. Dla: przez wstrzyknięcie informacji o stanie aplikacji sprawia, że dane ramek są bardziej przydatne, i dostarcza kontekst dla ramek, w których występuje zacinanie.
Ta statyczna metoda pobiera bieżącą
MetricsStateHolder
dla danej hierarchii widoków.
PerformanceMetricsState.getHolderForHierarchy(view: View): MetricsStateHolder
Można używać dowolnego widoku w aktywnej hierarchii. Ta funkcja sprawdza wewnętrznie,
czy istniejący obiekt Holder
jest z nim powiązany.
hierarchię widoków. Informacje te są przechowywane w widoku u góry
w hierarchii. Jeśli taki obiekt nie istnieje, getHolderForHierarchy()
tworzy go.
Statyczna metoda getHolderForHierarchy()
pozwala uniknąć konieczności zapisywania w pamięci podręcznej.
instancję posiadacza do późniejszego pobrania i ułatwia
istniejącego obiektu stanu z dowolnego miejsca w kodzie (lub nawet kodu biblioteki,
w innym przypadku nie mieliby dostępu do oryginalnej instancji).
Zwróć uwagę, że zwracana wartość jest obiektem posiadacza, a nie samym obiektem stanu. wartość obiektu State w obiekcie jest ustawiana wyłącznie przez JankStats. To jeśli aplikacja tworzy obiekt JankStats dla okna zawierającego hierarchię wyświetlania, obiekt stanu zostanie który został utworzony i ustawiony. W przeciwnym razie bez śledzenia informacji statystyki JankStats będą nie jest potrzebny obiekt stanu, nie jest też potrzebny w przypadku aplikacji czy biblioteki, do wstrzykiwania stanu.
Takie podejście umożliwia
pobrać obiekt, który JankStats może następnie uzupełnić. Kod zewnętrzny
może w dowolnym momencie poprosić o podanie jego właściciela. Rozmówcy mogą buforować lekkie urządzenie Holder
i używać go w dowolnym momencie do określenia stanu, w zależności od wartości jego
wewnętrzna właściwość state
, jak w przykładowym kodzie poniżej, gdzie stan jest ustawiony tylko
gdy właściwość stanu wewnętrznego posiadacza nie ma wartości null:
val metricsStateHolder = PerformanceMetricsState.getHolderForHierarchy(binding.root)
// ...
metricsStateHolder.state?.putState("Activity", javaClass.simpleName)
Aby kontrolować stan UI lub aplikacji, aplikacja może wprowadzić (lub usunąć) stan
za pomocą metod putState
i removeState
. JankStats rejestruje sygnaturę czasową
do tych połączeń. Jeśli ramka pokrywa się z czasem rozpoczęcia i zakończenia stanu,
JankStats podaje informacje wraz z danymi o czasie dla
ramki.
W przypadku każdego stanu dodaj 2 informacje: key
(kategorię stanu, np. „RecyclerView”) oraz value
(informacje na temat
co działo się w danym momencie, np. „przewijanie”).
Usuń stany za pomocą metody removeState()
, gdy ten stan nie jest określony
jest ważny przez dłuższy czas, by zapewnić, że nieprawidłowe lub wprowadzające w błąd informacje nie będą zgłaszane
z danymi klatek.
Wywołanie „putState()
” przy użyciu dodanego wcześniej numeru key
zastępuje
istniejące value
tego stanu z nowym stanem.
Wersja putSingleFrameState()
interfejsu stanowego API dodaje stan:
tylko raz – dla kolejnej zgłaszanej klatki. System automatycznie
zostanie wtedy usunięta, aby zapobiec przypadkowemu nieaktualnemu stanowi
kod. Pamiętaj, że nie ma odpowiednika w standardzie singleFrame
removeState()
, ponieważ JankStats automatycznie usuwa stany pojedynczej klatki.
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")
}
}
}
}
Pamiętaj, że klucz używany w stanach powinien być na tyle ważny, aby umożliwiać
jego późniejszej analizy. W szczególności, ponieważ stan z taką samą wartością key
jak w
zastąpi ona wcześniejszą wartość, spróbuj użyć
unikalne nazwy key
obiektów, które mogą mieć różne instancje w
aplikacji lub biblioteki. Na przykład aplikacja z pięcioma różnymi elementami RecyclerView
warto udostępnić możliwe do identyfikacji klucze dla każdego z nich, zamiast używać po prostu
RecyclerView
dla każdego z nich, a następnie nie będziesz mieć możliwości łatwego określenia w
danych wynikowych, do których odnoszą się dane ramki.
Heurystyka stymulująca
Aby dostosować wewnętrzny algorytm wykrywania zacinania, użyj funkcji
właściwość jankHeuristicMultiplier
.
Domyślnie system definiuje zacinanie jako ramkę, której renderowanie trwa dwa razy dłużej niż bieżącej częstotliwości odświeżania. Nie traktuje szarości jako niczego częstotliwość odświeżania, ponieważ informacje o czasie renderowania aplikacji nie są jasne. Dlatego lepiej jest dodać bufor i zawierać tylko raporty, gdy powodują zauważalne problemy z wydajnością.
Obie te wartości można zmienić w ten sposób odpowiednio do sytuacji lub w celu wymuszenia zacinania, niezbędną do przeprowadzenia testu.
Użycie w Jetpack Compose
Obecnie funkcja JankStats w funkcji Compose nie wymaga konfiguracji.
Aby zachować PerformanceMetricsState
podczas wprowadzania zmian w konfiguracji,
zapamiętaj go w taki sposób:
/**
* 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) }
}
Aby użyć statystyk Jank, dodaj do stateHolder
bieżący stan, jak pokazano tutaj:
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")
}
}
}
Szczegółowe informacje o używaniu JankStats w aplikacji Jetpack Compose znajdziesz na stronie znajdziesz w naszej przykładowej aplikacji wydajności.
Prześlij opinię
Podziel się z nami swoimi opiniami i pomysłami, korzystając z tych zasobów:
- Narzędzie do śledzenia błędów
- Zgłoś problemy, abyśmy mogli je naprawić. .
Polecane dla Ciebie
- Uwaga: tekst linku wyświetla się, gdy JavaScript jest wyłączony
- Tworzenie profili podstawowych {:#Creation-profile-rules}
- Argumenty instrumentacji mikrotestów
- Argumenty instrumentacji w ramach analizy porównawczej