Biblioteka JankStats

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:

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łania zwrotne i szybko zwraca je, aby 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: wstrzykiwanie 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. Ten 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ć.
.
. .