JankStats-Bibliothek

Mithilfe der JankStats-Bibliothek können Sie Leistungsprobleme in Anwendungen. Der Begriff Jank bezieht sich auf Anwendungsframes, deren Rendering zu lange dauert. und die JankStats-Bibliothek bietet Berichte zu den Verzögerungsstatistiken Ihrer App.

Rechte

JankStats baut auf den vorhandenen Funktionen der Android-Plattform auf, darunter: FrameMetrics API unter Android 7 (API-Level 24) und höher oder OnPreDrawListener Versionen. Diese Mechanismen können Anwendungen dabei helfen zu verfolgen, wie lange Frames abgeschlossen ist. Die JanksStats-Bibliothek bietet zwei zusätzliche Funktionen, dynamischer und einfacher zu nutzen: Verzögerungen und UI-Status.

Jank-Heuristik

Sie können FrameMetrics zwar verwenden, um die Frame-Dauer zu verfolgen, aber FrameMetrics Unterstützung bei der Ermittlung der tatsächlichen Verzögerung bieten. JankStats hat jedoch konfigurierbaren internen Mechanismen, um zu ermitteln, wann Verzögerungen auftreten. sofort nützlicher machen.

UI-Status

Häufig ist es wichtig, den Kontext von Leistungsproblemen in deiner App zu kennen. Wenn Sie beispielsweise eine komplexe Multiscreen-App entwickeln, die FrameMetrics verwendet, und feststellen, dass die Frames in Ihrer App häufig extrem stockend sind, diese Informationen in einen Kontext zu setzen, indem er weiß, wo das Problem aufgetreten ist, was die Nutzenden tun und wie sie reproduziert werden können.

JankStats löst dieses Problem durch die Einführung einer state-API, mit der Sie mit der Bibliothek kommunizieren, um Informationen zu den App-Aktivitäten bereitzustellen. Wann? JankStats protokolliert Informationen über einen fehlerhaften Frame und enthält den aktuellen Status die Anwendung in Verzögerungsberichten.

Nutzung

Um mit JankStats zu arbeiten, müssen Sie die Bibliothek für jedes einzelne Element instanziieren und aktivieren. Window Jedes JankStats-Objekt erfasst Daten. nur innerhalb eines Window. Zum Instanziieren der Bibliothek ist eine Window-Instanz erforderlich zusammen mit OnFrameListener Listener. Mit beiden werden Messwerte an den Client gesendet. Der Listener wird mit FrameData in jedem Frame und folgende Details:

  • Beginn des Frames
  • Werte für die Dauer
  • Gibt an, ob der Frame als Verzögerung betrachtet werden soll
  • Eine Reihe von String-Paaren, die Informationen zum Anwendungsstatus enthalten im Frame

Um JankStats nützlicher zu machen, sollten Anwendungen die Bibliothek mit relevante Informationen zum Status der Benutzeroberfläche für die Berichterstellung in FrameData. Sie schaffen das über die PerformanceMetricsState API (nicht direkt JankStats), bei der die gesamte Statusverwaltungslogik und APIs veröffentlicht.

Initialisierung

Um die JankStats-Bibliothek zu verwenden, fügen Sie zuerst die JankStats-Abhängigkeit zu Ihrem Gradle-Datei:

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

Initialisieren und aktivieren Sie anschließend JankStats für jedes Window. Außerdem sollten Sie JankStats-Tracking, wenn eine Aktivität in den Hintergrund geht Erstellen und aktivieren JankStats-Objekt in Ihren Activity-Überschreibungen:

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)
        // ...
    }

Im obigen Beispiel werden Statusinformationen zur aktuellen Aktivität, nachdem das JankStats-Objekt erstellt wurde. Alle zukünftigen FrameData-Berichte die für dieses JankStats-Objekt erstellt wurden, enthalten jetzt auch Aktivitätsdaten.

Die Methode JankStats.createAndTrack verweist auf eine Window. -Objekt, das ein Proxy für die View-Hierarchie innerhalb dieses Window-Elements ist, sowie für Window selbst. jankFrameListener wird im gleichen verwendeten Thread aufgerufen diese Informationen von der Plattform intern an JankStats weiterleiten.

Um Tracking und Berichterstellung für ein JankStats-Objekt zu aktivieren, isTrackingEnabled = true anrufen. Es ist zwar standardmäßig aktiviert, Durch das Pausieren einer Aktivität wird das Tracking deaktiviert. Aktivieren Sie in diesem Fall die bevor Sie fortfahren. Wenn Sie das Tracking beenden möchten, rufen Sie isTrackingEnabled = false auf.

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

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

Berichte

Die JankStats-Bibliothek dokumentiert das gesamte Daten-Tracking für jeden Frame an den OnFrameListener für aktivierte JankStats-Objekte. Apps können Daten speichern und zusammenfassen um Daten zu einem späteren Zeitpunkt hochzuladen. Weitere Informationen finden Sie in Sehen Sie sich die Beispiele im Abschnitt Aggregation an.

Du musst die OnFrameListener erstellen und bereitstellen, damit deine App die Berichte zu den einzelnen Frames. Dieser Listener wird auf jedem Frame aufgerufen, um in Apps verschieben.

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())
}

Der Listener gibt pro Frame Informationen zu Verzögerungen mit der Methode FrameData-Objekt. Dieses enthält die folgenden Informationen über den angeforderten Frame:

  • isjank: Boolescher Parameter, der angibt, ob im Frame eine Verzögerung aufgetreten ist.
  • frameDurationUiNanos: Frame-Dauer (in Nanosekunden).
  • frameStartNanos: Startzeit des Frames (in Nanosekunden).
  • states: Status der App während des Frames.

Wenn du Android 12 (API-Level 31) oder höher verwendest, kannst du Verwenden Sie folgenden Code, um mehr Daten zur Framedauer bereitzustellen:

Verwenden Sie StateInfo in der Listener zum Speichern von Informationen zum Anwendungsstatus.

Beachten Sie, dass OnFrameListener in demselben Thread aufgerufen wird, der intern verwendet wird, um die Informationen pro Frame an JankStats senden. Bei Android-Version 6 (API-Level 23) und niedriger ist dies der Hauptthread (UI-Thread). Unter Android Version 7 (API-Level 24) und höher ist es die -Thread, der für FrameMetrics erstellt und verwendet wird. In beiden Fällen ist es wichtig, den Callback verarbeiten und schnell zurückkehren, um Leistungsprobleme auf zu diesem Thread.

Beachten Sie außerdem, dass das im Rückruf gesendete FrameData-Objekt bei jedem um zu verhindern, dass neue Objekte für die Berichterstellung zugewiesen werden müssen. Das bedeutet, dass Sie diese Daten kopieren und an anderer Stelle im Cache speichern müssen, da dieses Objekt wird als „statle“ und „veraltet“ betrachtet, sobald der Callback zurückgegeben wird.

Aggregieren

Sie möchten wahrscheinlich, dass Ihr App-Code die Daten pro Frame aggregiert, sodass Sie können die Informationen nach eigenem Ermessen speichern und hochladen. Obwohl Details zum Speichern und Hochladen von Daten werden nicht im Umfang der Alpha JankStats API behandelt. können Sie eine vorläufige Aktivität zum Aggregieren von Daten pro Frame in einer größeren Sammlung mit JankAggregatorActivity in unserer GitHub-Repository.

JankAggregatorActivity verwendet die JankStatsAggregator-Klasse, um eigene Berichte übereinanderzulegen neben dem OnFrameListener-Mechanismus von JankStats zur Bereitstellung einer einer übergeordneten Abstraktionsebene, um nur eine Sammlung von Informationen mehrere Frames umfasst.

Anstatt ein JankStats-Objekt direkt zu erstellen, JankAggregatorActivity Erstellt einen JankStatsAggregator. , das intern ein eigenes JankStats-Objekt erstellt:

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)
    }

In JankAggregatorActivity wird ein ähnlicher Mechanismus zum Pausieren und Fortsetzen des Trackings, wobei das Ereignis pause() als Signal für das Problem hinzugefügt wird einen Bericht mit einem Aufruf von issueJankReport(), da Änderungen des Lebenszyklus Zeit, um den Status der Verzögerung in der Anwendung zu erfassen:

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
}

Der obige Beispielcode ist alles, was eine App benötigt, um JankStats zu aktivieren und Frame-Daten.

Status verwalten

Es ist möglich, dass Sie andere APIs aufrufen möchten, um JankStats anzupassen. Für werden Frame-Daten durch das Einfügen von App-Status-Informationen hilfreicher, und bietet Kontext für die Frames, in denen Verzögerungen auftreten.

Diese statische Methode ruft den aktuellen MetricsStateHolder für eine bestimmte View-Hierarchie.

PerformanceMetricsState.getHolderForHierarchy(view: View): MetricsStateHolder

Jede Ansicht in einer aktiven Hierarchie kann verwendet werden. Intern wird überprüft, ob ob ein vorhandenes Holder-Objekt mit diesem verknüpft ist Hierarchieansicht. Diese Informationen werden in einer Ansicht oben im Cache gespeichert. Hierarchie. Falls kein solches Objekt vorhanden ist, erstellt getHolderForHierarchy() eines.

Mit der statischen Methode getHolderForHierarchy() müssen Sie nicht zwischen Holder-Instanz an einen anderen Ort für den späteren Abruf zu verschieben. Dies erleichtert das Abrufen ein vorhandenes Zustandsobjekt an einer beliebigen Stelle im Code (oder sogar Bibliothekscode, der sonst keinen Zugriff auf die ursprüngliche Instanz hätten.)

Beachten Sie, dass der Rückgabewert ein Halteobjekt ist, nicht das Statusobjekt selbst. Die -Wert des state-Objekts innerhalb des Halters wird nur von JankStats festgelegt. Das Wenn eine Anwendung ein JankStats-Objekt für das Fenster erstellt, das diese ist das Zustandsobjekt festgelegt und festgelegt wurden. Ohne dass JankStats die Informationen erfasst, ist weder das "state"-Objekt noch eine App oder Bibliothek um den Zustand einzufügen.

Dieser Ansatz ermöglicht es, einen Halter abzurufen, den JankStats anschließend füllen kann. Externer Code jederzeit nach dem Inhaber fragen kann. Aufrufer können die einfache Holder-Datei im Cache speichern -Objekt und können es jederzeit zum Festlegen des Status verwenden, je nach dem Wert seines Internes state-Attribut wie im Beispielcode unten, bei dem nur der Status Die interne Statuseigenschaft des Inhabers ist nicht null:

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

Zur Steuerung des UI-/App-Status kann eine App einen Status einfügen (oder entfernen) mit den Methoden putState und removeState. JankStats protokolliert den Zeitstempel für diesen Anrufen. Wenn sich ein Frame mit der Start- und Endzeit des Status überschneidet, JankStats liefert diese Informationen zusammen mit den Zeitdaten für die Frame.

Fügen Sie für jeden Bundesstaat zwei Informationen hinzu: key (eine Zustandskategorie, z. B. "RecyclerView") und value (Informationen über was zu dieser Zeit geschah, z. B. „Scrollen“.

Entfernen Sie Status mithilfe der Methode removeState(), wenn dieser Status nicht vorhanden ist um sicherzustellen, dass keine falschen oder irreführenden Informationen gemeldet werden. mit Frame-Daten.

Wenn putState() mit einer zuvor hinzugefügten key aufgerufen wird, ersetzt dies das value dieses Bundesstaates durch den neuen Bundesstaat ersetzt.

Die putSingleFrameState()-Version der State API fügt einen Status hinzu, der wird nur einmal beim nächsten gemeldeten Frame protokolliert. Das System hat automatisch und entfernt es anschließend wieder. So vermeiden Sie, Ihren Code. Es gibt kein SingleFrame-Äquivalent zu removeState(), da JankStats die Zustände einzelner Frames automatisch entfernt.

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")
            }
        }
    }
}

Beachten Sie, dass der Schlüssel für Statusangaben so aussagekräftig sein muss, dass er spätere Analyse. Da ein Bundesstaat mit derselben key wie ein Bundesstaat, den vorherigen Wert ersetzt, sollten Sie versuchen, eindeutige key-Namen für Objekte, die unterschiedliche Instanzen in Ihrem App oder Bibliothek aus. Eine App mit fünf verschiedenen RecyclerViews eindeutige Schlüssel für jeden Schlüssel bereitstellen, anstatt einfach nur RecyclerView für jeden einzelnen Wert und kann dann im auf welche Instanz sich die Frame-Daten beziehen.

Jank-Heuristik

Um den internen Algorithmus zur Bestimmung, was als Verzögerung betrachtet wird, anzupassen, verwenden Sie Das Attribut jankHeuristicMultiplier

Standardmäßig definiert das System Verzögerung als einen Frame, dessen Rendering doppelt so lang dauert wie die aktuelle Aktualisierungsrate. Verzögerung wird nicht als etwas über da die Informationen zur Renderingzeit der App nicht löschen. Daher ist es besser, einen Puffer hinzuzufügen und nur wenn sie spürbare Leistungsprobleme verursachen.

Beide Werte können mit diesen Methoden an die Situation angepasst werden. der App genauer an. Sie können auch testen, ob Verzögerungen auftreten oder nicht. die für den Test erforderlich sind.

Verwendung in Jetpack Compose

Derzeit ist nur sehr wenig Einrichtung erforderlich, um JankStats in Compose zu verwenden. Um die PerformanceMetricsState bei Konfigurationsänderungen beizubehalten, sich das so zu merken:

/**
 * 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) }
}

Um JankStats zu verwenden, fügen Sie den aktuellen Status zu stateHolder hinzu, wie hier gezeigt:

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")
        }
    }
}

Ausführliche Informationen zur Verwendung von JankStats in Ihrer Jetpack Compose-Anwendung finden Sie unter finden Sie in unserer Beispiel-App zur Leistungssteigerung.

Feedback geben

Wir freuen uns über dein Feedback und deine Ideen:

Problemverfolgung
Melde Probleme, damit wir sie beheben können.