Ś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. Awaria w tych fazach może spowodować powstanie śladów stosu, które są trudne do odczytania, co utrudnia wskazanie dokładnej funkcji lub wiersza kodu, który spowodował awarię.

Dodawanie informacji o źródle do śladów stosu

Aby zwiększyć czytelność śladu stosu, interfejs API z możliwością włączenia udostępnia bardziej szczegółowe informacje o lokalizacji awarii, w tym nazwy i lokalizacje elementów kompozycyjnych, co umożliwia:

  • Skuteczne identyfikowanie i usuwanie przyczyn awarii
  • Izolowanie awarii w celu uzyskania powtarzalnych próbek
  • Badanie awarii, w przypadku których wcześniej wyświetlane były tylko wewnętrzne ramki stosu

Środowisko wykonawcze Compose może wykryć miejsce awarii w kompozycji i odtworzyć ślad stosu na podstawie hierarchii @Composable. Zrzut stosu jest dołączany w przypadku awarii w tych miejscach:

  • Kompozycja
  • DisposableEffect i LaunchedEffect (z wyjątkiem onDispose lub anulowania)
  • Coroutines launched in rememberCoroutineScope
  • Mierzenie, rozmieszczanie i rysowanie przejazdów

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 śladzie stosu są zbierane prawidłowo.

Dostępne są 4 opcje ComposeStackTraceMode:

  • Auto: zalecana opcja, ponieważ używa GroupKeys, jeśli aplikacja jest zminimalizowana, a None w przeciwnym razie.
  • GroupKeys: zrzuty stosu są tworzone w przypadku zminimalizowanych aplikacji. Informacje o kluczu grupy są zachowywane nawet po minifikacji i używane razem z plikiem mapowania ProGuard wygenerowanym przez kompilator Compose i R8 do odtworzenia przybliżonej lokalizacji funkcji @Composable. Te ślady stosu są mniej precyzyjne i zoptymalizowane pod kątem unikania dodatkowej pracy w czasie działania. Kompilator Compose obsługuje generowanie dodatkowych map R8 od wersji Kotlin 2.3.0.
  • SourceInformation: przydatne w przypadku wersji niepomniejszonych, zbiera informacje o źródle i dodaje je do zrzutu stosu. Wyniki są dokładniejsze, ale wiążą się ze znacznym kosztem wydajności, podobnym do dołączenia inspektora układu. Są one tworzone z myślą o używaniu w wersjach debugowania aplikacji, aby uzyskiwać dokładne odczyty dotyczące awarii, które wymagają więcej informacji o swojej 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żyjesz opcji SourceInformation, ślad stosu pojawi się na liście pominiętych wyjątków jako DiagnosticComposeException:

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 śladu 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 następuje po awarii, dane tabeli slotów mogą być niekompletne i nie zawierać numeru wiersza. ReusableComposeNoderemember nie generują informacji o źródle, więc w ramkach stosu tych funkcji zobaczysz <unknown line>.

Śledzenie stosu kluczy grupy

Śledzenie stosu oparte na GroupKeys może z założenia wskazywać tylko pierwszy wiersz funkcji @Composable. Nie zawierają też danych dotyczących funkcji, które nie tworzą grupy (np. funkcji wbudowanych lub funkcji zwracających wartość inną niż Unit).

Awarie podczas zbierania zrzutów stosu

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

Zgłaszaj wszelkie pominięte awarie lub niespójności śladu stosu do komponentu Compose Runtime.