Stack traces no Compose

O Jetpack Compose executa o código em várias fases diferentes, o que faz com que algumas partes da função @Composable sejam executadas separadamente umas das outras. Falhas nessas fases podem resultar em stack traces difíceis de decifrar, o que dificulta a identificação da função ou linha de código exata que causou a falha.

Adicionar informações de origem aos stack traces

Para melhorar a legibilidade do stack trace, uma API de ativação fornece detalhes mais avançados sobre o local da falha, incluindo nomes e locais de composição, permitindo que você:

  • Identificar e resolver as causas de falhas de forma eficiente
  • Isolar falhas para amostras reproduzíveis
  • Investigue falhas que antes mostravam apenas frames de pilha internos

O ambiente de execução do Compose pode detectar o local da falha na composição e reconstruir um stack trace com base na hierarquia de @Composable. O stack trace é anexado para falhas em:

  • Composição
  • DisposableEffect e LaunchedEffect (exceto para onDispose ou cancelamento)
  • Corrotinas iniciadas em rememberCoroutineScope
  • Passagens de medição, layout e desenho

Para ativar esse recurso, adicione as seguintes linhas ao ponto de entrada do aplicativo:

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

O ideal é realizar essa configuração antes de criar qualquer composição para verificar se as informações de stack trace são coletadas corretamente.

Há quatro opções para o ComposeStackTraceMode:

  • Auto: opção recomendada, porque usa GroupKeys se o app for minificado e None caso contrário.
  • GroupKeys: os stack traces são criados para apps minificados. As informações da chave de grupo são mantidas mesmo após a minificação e são usadas com o arquivo de mapeamento do ProGuard emitido pelo compilador do Compose e pelo R8 para reconstruir um local aproximado das funções @Composable. Esses stack traces são menos precisos e otimizados para evitar trabalho extra no ambiente de execução. O compilador do Compose é compatível com a emissão de mais mapeamentos do R8 a partir do Kotlin 2.3.0.
  • SourceInformation: útil para builds não minificados, coleta informações de origem e as adiciona ao stack trace. Os resultados são mais precisos, mas geram um custo de desempenho significativo, semelhante ao de anexar o Layout Inspector. Eles são criados para serem usados em versões de depuração dos apps para receber leituras precisas sobre uma falha que requer mais informações sobre o local. As informações de origem são removidas de apps minificados para otimizar o tamanho e o desempenho do binário.
  • None: nenhum detalhe extra de stack trace adicionado.

Ao usar a opção SourceInformation, o stack trace aparece como um DiagnosticComposeException na lista de exceções suprimidas:

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)

Limitações conhecidas

Há alguns problemas conhecidos com frames de stack trace:

Stack traces de informações da fonte

Números de linha ausentes (<unknown line>) no primeiro frame de pilha para falhas na composição. Como a introspecção das informações de origem ocorre após uma falha, os dados da tabela de slots podem estar incompletos e descartar o número da linha. ReusableComposeNode e remember não produzem informações de origem, então você vai encontrar <unknown line> nos frames de pilha dessas funções.

Stack traces de chaves de grupo

Os stack traces baseados em GroupKeys só podem apontar para a primeira linha da função @Composable por design. Eles também não contêm dados para funções que não produzem um grupo (como funções inline ou que não retornam Unit).

Falhas na coleta de stack traces

Se a coleta de stack trace falhar por qualquer motivo, essa exceção será anexada como uma exceção suprimida em vez de DiagnosticComposeException.

Informe as falhas suprimidas ou as inconsistências de stack trace ao componente de ambiente de execução do Compose.