Śledzenie stosu w Compose

Jetpack Compose wykonuje kod w kilku różnych fazach, co powoduje, że niektóre części funkcji @Composable są wykonywane oddzielnie od siebie. Awarie na tych etapach mogą powodować zrzuty stosu, które są trudne do rozszyfrowania, co utrudnia określenie dokładnej funkcji lub wiersza kodu, który spowodował awarię.

Dodawanie informacji o źródle do zrzutów stosu

Aby zwiększyć czytelność zrzutu stosu, interfejs API, który wymaga akceptacji, podaje bardziej szczegółowe informacje o lokalizacji awarii, w tym nazwy i lokalizacje funkcji kompozycyjnych, co umożliwia:

  • Skuteczne identyfikowanie i rozwiązywanie problemów ze źródłami awarii
  • Izolowanie awarii w celu uzyskania powtarzalnych próbek
  • Analizowanie awarii, które wcześniej wyświetlały tylko wewnętrzne ramki stosu

Środowisko wykonawcze Compose może wykryć lokalizację awarii w kompozycji i zrekonstruować zrzut stosu na podstawie hierarchii @Composable. Zrzut stosu jest dołączany w przypadku awarii w:

  • Kompozycja
  • DisposableEffect i LaunchedEffect (z wyjątkiem onDispose lub anulowania)
  • Korutyny uruchomione w rememberCoroutineScope
  • Operacje pomiaru, układu i rysowania

Aby włączyć tę funkcję, dodaj te wiersze do punktu wejścia aplikacji:

// Enable stack traces at application level: onCreate
class SampleStackTracesEnabledApp : Application() {

    override fun onCreate() {
        super.onCreate()
        // Enable Compose stack traces for minified builds only.
        Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.Auto)

        // Alternatively:
        // Enable verbose Compose stack traces for local debugging
        Composer.setDiagnosticStackTraceMode(ComposeStackTraceMode.SourceInformation)
    }
}

Najlepiej przeprowadzić tę konfigurację przed utworzeniem jakichkolwiek kompozycji, aby sprawdzić, czy informacje o zrzucie stosu są prawidłowo zbierane.

Dostępne są 4 opcje ComposeStackTraceMode:

  • Auto: zalecana opcja, ponieważ używa GroupKeys, jeśli aplikacja jest zminimalizowana, a w przeciwnym razie – None.
  • GroupKeys: zrzuty stosu są tworzone dla aplikacji zminimalizowanych. Informacje o kluczu grupy są zachowywane nawet po minifikacji i są używane razem z plikiem mapowania proguard wygenerowanym przez kompilator Compose i R8 do odtworzenia przybliżonej lokalizacji funkcji @Composable. Te zrzuty stosu są mniej precyzyjne i zoptymalizowane pod kątem unikania dodatkowej pracy w czasie działania. Kompilator Compose obsługuje generowanie dodatkowych mapowań R8 od wersji Kotlin 2.3.0.
  • SourceInformation: przydatne w przypadku kompilacji niezminimalizowanych, zbiera informacje o źródle i dodaje je do zrzutu stosu. Wyniki są dokładniejsze, ale wiążą się ze znacznym obciążeniem wydajności, podobnym do tego, które występuje przy dołączaniu narzędzia Layout Inspector. Są one tworzone do użycia w wersjach debugowania aplikacji, aby uzyskać dokładne odczyty dotyczące awarii, która wymaga więcej informacji o jej lokalizacji. Informacje o źródle są usuwane ze zminimalizowanych aplikacji, aby zoptymalizować rozmiar i wydajność plików binarnych.
  • None: nie dodano żadnych dodatkowych szczegółów zrzutu stosu.

Gdy używasz opcji SourceInformation, zrzut stosu pojawia się jako DiagnosticComposeException na liście pominiętych wyjątków:

java.lang.IllegalStateException: Test layout error
    at <original trace>
Suppressed: androidx.compose.runtime.DiagnosticComposeException:
Composition stack when thrown:
    at ReusableComposeNode(Composables.kt:<unknown line>)
    at Layout(Layout.kt:79)
    at <lambda>(TempErrorsTest.kt:164)
    at <lambda>(BoxWithConstraints.kt:66)
    at ReusableContentHost(Composables.kt:164)
    at <lambda>(SubcomposeLayout.kt:514)
    at SubcomposeLayout(SubcomposeLayout.kt:114)
    at SubcomposeLayout(SubcomposeLayout.kt:80)
    at BoxWithConstraints(BoxWithConstraints.kt:64)
    at SubcomposeLayoutErrorComposable(TempErrorsTest.kt:164)
    at <lambda>(TempErrorsTest.kt:86)
    at Content(ComposeView.android.kt:430)
    at <lambda>(ComposeView.android.kt:249)
    at CompositionLocalProvider(CompositionLocal.kt:364)
    at ProvideCommonCompositionLocals(CompositionLocals.kt:193)
    at <lambda>(AndroidCompositionLocals.android.kt:113)
    at CompositionLocalProvider(CompositionLocal.kt:364)
    at ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:102)
    at <lambda>(Wrapper.android.kt:141)
    at CompositionLocalProvider(CompositionLocal.kt:384)
    at <lambda>(Wrapper.android.kt:140)

Znane ograniczenia

Istnieje kilka znanych problemów z ramkami zrzutu stosu:

Zrzuty stosu informacji o źródle

Brak numerów wierszy (<unknown line>) w pierwszej ramce stosu w przypadku awarii w kompozycji. Ponieważ introspekcja informacji o źródle odbywa się po awarii, dane tabeli slotów mogą być niekompletne i nie zawierać numeru wiersza. ReusableComposeNode i remember nie generują informacji o źródle, więc w ramkach stosu tych funkcji zobaczysz <unknown line>.

Zrzuty stosu kluczy grupowania

Zrzuty stosu oparte na GroupKeys mogą wskazywać tylko pierwszy wiersz funkcji @Composable. Nie zawierają też żadnych danych dotyczących funkcji, które nie tworzą grupy (takich jak funkcje wbudowane lub funkcje, które nie zwracają jednostki).

Awarie zbierania zrzutów stosu

Jeśli zbieranie zrzutów stosu z jakiegoś powodu ulegnie awarii, ten wyjątek zostanie dołączony jako wyjątek pominięty, a nie DiagnosticComposeException.

Zgłaszaj wszelkie pominięte awarie lub niespójności zrzutu stosu do komponentu środowiska wykonawczego Compose.