Quando confrontada com uma classe instável que causa desempenho problemas, você deve torná-lo estável. Este documento descreve várias técnicas que você pode usar para isso.
Ativar a opção "Pular com bloqueio"
Primeiro, tente ativar o modo de pulo forte. Modo avançado de pular permite que elementos combináveis com parâmetros instáveis sejam ignorados e é o método mais fácil para corrigir problemas de desempenho causados pela estabilidade.
Consulte Ignorar forte para mais informações.
Tornar a classe imutável
Você também pode tentar tornar uma classe instável completamente imutável.
- Imutável: indica um tipo em que o valor de qualquer propriedade nunca pode
mudar depois que uma instância desse tipo é construída, e todos os métodos são
referencialmente transparente.
- Verifique se todas as propriedades da classe são
val
em vez devar
. e de tipos imutáveis. - Os tipos primitivos, como
String, Int
eFloat
, são sempre imutáveis. - Se isso for impossível, use o estado do Compose para propriedades mutáveis.
- Verifique se todas as propriedades da classe são
- Estável: indica um tipo mutável. O ambiente de execução do Compose não tomar ciência se e quando qualquer propriedade pública ou método do tipo resultaria em resultados diferentes de uma invocação anterior.
Coleções imutáveis
As coleções são um motivo comum pelo qual o Compose considera uma classe instável. Conforme observado
Na página Diagnosticar problemas de estabilidade, o compilador do Compose
não é possível ter certeza de que coleções como List, Map
e Set
estão
são imutáveis e, portanto, os marca como instáveis.
Para resolver isso, use coleções imutáveis. O compilador do Compose inclui suporte para Coleções imutáveis do Kotlinx. Esses as coleções são imutáveis e o compilador do Compose as trata. como tal. Essa biblioteca ainda está na versão Alfa, então podem ocorrer mudanças na API dela.
Considere novamente esta classe instável do artigo Diagnosticar estabilidade de problemas:
unstable class Snack {
…
unstable val tags: Set<String>
…
}
É possível tornar tags
estável usando uma coleção imutável. Na turma, mude
tipo de tags
para ImmutableSet<String>
:
data class Snack{
…
val tags: ImmutableSet<String> = persistentSetOf()
…
}
Depois disso, todos os parâmetros da classe serão imutáveis, e o Compose o compilador marca a classe como estável.
Adicione as anotações Stable
ou Immutable
.
Um caminho possível para resolver problemas de estabilidade é anotar em classes instáveis
com @Stable
ou @Immutable
.
Anotar uma classe substitui o que o compilador faria
inferir sobre sua classe. É semelhante à
Operador !!
no Kotlin. Você deve ser muito
cuidado com o uso dessas anotações. Como modificar o comportamento do compilador
pode levar a bugs imprevistos, como a recomposição do elemento combinável ao
como você espera.
Se for possível tornar sua classe estável sem uma anotação, você deve se esforçar para alcançar a estabilidade.
O snippet a seguir fornece um exemplo mínimo de uma classe de dados anotada como imutável:
@Immutable
data class Snack(
…
)
Se você usar a anotação @Immutable
ou @Stable
, o compilador do Compose
marca a classe Snack
como estável.
Classes com anotações em coleções
Considere um elemento combinável que inclua um parâmetro do tipo List<Snack>
:
restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
…
unstable snacks: List<Snack>
…
)
Mesmo se você anotar Snack
com @Immutable
, o compilador do Compose ainda vai marcar
o parâmetro snacks
em HighlightedSnacks
como instável.
Quando se trata de tipos de coleções, os parâmetros enfrentam o mesmo problema que as classes,
o compilador do Compose sempre marca um parâmetro do tipo List
como instável, mesmo
quando é uma coleção de tipos estáveis.
Não é possível marcar um parâmetro individual como estável, nem anotar uma combinável para que seja sempre pulável. Há vários caminhos para frente.
Há várias maneiras de contornar o problema de coleções instáveis. As subseções abaixo apresentam essas diferentes abordagens.
Arquivo de configuração
Se você estiver feliz em cumprir o contrato de estabilidade em sua base de código,
você pode considerar as coleções Kotlin como estáveis adicionando
kotlin.collections.*
para seu
arquivo de configuração de estabilidade.
Coleção imutável
Para ter segurança de imutabilidade no momento da compilação, você pode
usar uma coleção imutável do Kotlinx, em vez de List
.
@Composable
private fun HighlightedSnacks(
…
snacks: ImmutableList<Snack>,
…
)
Wrapper
Se não for possível usar uma coleção imutável, você poderá criar a própria. Para isso,
encapsular o List
em uma classe estável com anotação. Um wrapper genérico provavelmente
a melhor opção para isso, dependendo de suas necessidades.
@Immutable
data class SnackCollection(
val snacks: List<Snack>
)
Em seguida, ele pode ser usado como o tipo do parâmetro no elemento combinável.
@Composable
private fun HighlightedSnacks(
index: Int,
snacks: SnackCollection,
onSnackClick: (Long) -> Unit,
modifier: Modifier = Modifier
)
Solução
Depois de usar uma dessas abordagens, o compilador do Compose agora marca a
HighlightedSnacks
combinável como skippable
e restartable
.
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
stable index: Int
stable snacks: ImmutableList<Snack>
stable onSnackClick: Function1<Long, Unit>
stable modifier: Modifier? = @static Companion
)
Durante a recomposição, o Compose agora pode pular HighlightedSnacks
se nenhuma
de entrada mudaram.
Arquivo de configuração de estabilidade
A partir do Compose Compiler 1.5.5, um arquivo de configuração de classes para
considerados estáveis podem ser fornecidos no tempo de compilação. Isso permite considerar
classes que você não controla, como as da biblioteca padrão.
como LocalDateTime
, como estável.
O arquivo de configuração é um arquivo de texto simples com uma classe por linha. Comentários, caracteres curinga simples e duplos são aceitos. Confira abaixo um exemplo de configuração:
// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider kotlin collections stable
kotlin.collections.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>
Para ativar esse recurso, transmita o caminho do arquivo de configuração para o Compose do compilador.
Groovy
kotlinOptions {
freeCompilerArgs += [
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
project.absolutePath + "/compose_compiler_config.conf"
]
}
Kotlin
kotlinOptions {
freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
"${project.absolutePath}/compose_compiler_config.conf"
)
}
Como o compilador do Compose é executado em cada módulo do projeto separadamente, é possível fornecer configurações diferentes para módulos diferentes, se necessário. Como alternativa, tenha um de rede no nível raiz do seu projeto e transmita esse caminho a cada mais tarde neste módulo.
Vários módulos
Outro problema comum envolve a arquitetura com vários módulos. O compilador do Compose só podem inferir se uma classe é estável se todos os tipos não primitivos as referências são explicitamente marcadas como estáveis ou em um módulo que foi também foi criado com o compilador do Compose.
Se a camada de dados estiver em um módulo separado da camada de interface, que é o recomendada, isso pode ser um problema que você encontra.
Solução
Para resolver esse problema, escolha uma das seguintes abordagens:
- Adicione as classes ao seu arquivo de configuração do compilador.
- Ativar o compilador do Compose nos módulos da camada de dados ou adicionar uma tag às classes
com
@Stable
ou@Immutable
, quando apropriado.- Isso envolve adicionar uma dependência do Compose à camada de dados. No entanto,
isso depende apenas do ambiente de execução do Compose, não do
Compose-UI
:
- Isso envolve adicionar uma dependência do Compose à camada de dados. No entanto,
isso depende apenas do ambiente de execução do Compose, não do
- No módulo da interface, envolva as classes da camada de dados em um wrapper específico da interface classes.
O mesmo problema também ocorre ao usar bibliotecas externas, se elas não usarem o Compilador do Compose.
Nem todos os elementos combináveis devem ser puláveis
Ao trabalhar na correção de problemas de estabilidade, não tente fazer todas combinável e pulável. Tentar fazer isso pode levar a uma otimização prematura que introduz mais problemas do que corrige.
Há muitas situações em que ser pulável não tem nenhum benefício real e podem gerar códigos difíceis de manter. Exemplo:
- Um elemento combinável que não é recomposto com frequência ou que não é recomposto.
- Um elemento combinável que, por si só, chama elementos combináveis puláveis.
- Um elemento combinável com um grande número de parâmetros com custo igual a e implementações. Nesse caso, o custo de verificar se algum parâmetro tem mudou pode superar o custo de uma recomposição barata.
Quando um elemento combinável é pulável, ele adiciona uma pequena sobrecarga que pode não valer a pena reimplantá-lo. É possível até mesmo anotar que o elemento combinável é não reiniciável em casos em que você determina que ser reinicializável é maior do que vale a pena.