Tenho uma classe de dados no Jetpack Compose que representa um carro:
data class Car(
val id: Int = 0,
val brand: String = "",
val model: String = "",
val year: Int = 2020
)
No meu composable, eu atualizo as propriedades de marca e modelo dessa classe de dados com base na entrada do usuário em componentes TextField. Atualmente, estou usando a função copy() toda vez que o usuário digita algo, assim:
@Composable
fun CarScreen() {
var car by remember { mutableStateOf(Car()) }
Column {
TextField(
value = car.brand,
onValueChange = { newBrand ->
car = car.copy(brand = newBrand) // Using `copy()`
},
label = { Text("Brand") }
)
TextField(
value = car.model,
onValueChange = { newModel ->
car = car.copy(model = newModel) // Using `copy()`
},
label = { Text("Model") }
)
}
}
Preocupação:
Estou preocupado que chamar copy() em cada alteração de texto pode levar a problemas de desempenho, pois cria uma nova instância do objeto Car toda vez que o usuário digita em um TextField. Isso acontece com cada pressionamento de tecla e, em um aplicativo ou formulário maior com muitos campos, isso pode se tornar ineficiente.
Minhas perguntas:
- Chamar copy() em cada alteração de TextField é ineficiente em termos de desempenho no Jetpack Compose? Essa criação e recomposição constantes de objetos causariam degradação perceptível de desempenho, especialmente em modelos de dados maiores ou cenários de entrada frequentes?
- Quais são as melhores abordagens para lidar com mudanças frequentes de entrada de texto sem ter que usar copy() o tempo todo, ao mesmo tempo em que garante que o Compose possa recompor a UI quando necessário? Quero manter uma UI reativa sem criação excessiva de objetos.
E se houvesse uma maneira de, em vez de copiar o objeto, podermos alterar os valores da classe Data diretamente e compor automaticamente acionar a recomposição?
Com Views, foi recomendado não instanciar novos objetos, especialmente em
onDraw
funções que são chamadas várias vezes.No entanto, no Jetpack Compose, sem saltos fortes , classes de dados com parâmetros mutáveis são desencorajadas porque se a composição ocorrer em funções de escopo que tenham parâmetros instáveis, para classes de dados com parâmetros mutáveis ou classes instáveis de bibliotecas externas, ou outro módulo no seu projeto que não estenda o compilador Compose, ele acionará a recomposição para essa função.
https://developer.android.com/develop/ui/compose/performance/stability#mutable-objects
Na função acima, se você usar uma classe de dados com parâmetros mutáveis, quando a linha selecionada for alterada, o mesmo acontecerá com ContactDetails, mesmo que o contato não tenha sido alterado, porque ContactDetails tem uma entrada instável.
E não há sobrecarga de desempenho observável com a cópia de objetos como car ou mesmo datas com valores primitivos ou classes que contenham valores primitivos ou Strings. Você pode querer considerar apenas se sua classe de dados contém big data, como formato Bitmap ou Base64 de imagem, e mesmo para esse caso, tem que ser alguma cópia profunda.
Mas a função copy do Kotlin é uma cópia superficial . Ela só copia referências a objetos se você não criar novas instâncias.
Impressões
No entanto, se você ainda quiser otimizá-lo não criando um novo objeto de suporte, você pode usar vars com a anotação @Stable, que não é necessária com pulos fortes, o que impediria a recomposição da sua função se suas entradas não tivessem mudado quando outra
State
ou uma mudança de entradas da função pai acionasse a recomposição no escopo pai.E acione a recomposição quando as propriedades do seu carro mudarem, como
Usando outra SnapshotMutationPolicy
A política padrão do MutableState é
structuralEqualityPolicy()
que verifica se o novovalue
que você definiu é==
para o anterior. Em classes de dados, equals é determinado por parâmetros do construtor primário.Ao alterar esta política, você pode acionar a recomposição mesmo com o mesmo objeto, como
Como você pode ver com
neverEqualPolicy()
a política, você pode acionar a recomposição atribuindo o mesmo objeto.Aplica-se a qualquer classe. Você pode acionar a recomposição definindo o valor do contador para o mesmo valor.
Usando uma classe com MutableStates
Essa abordagem é amplamente usada nas classes rememberXState e no exemplo Jetsnack do Google.
ScrollState , estado de pesquisa do jetsnack
Você simplesmente divide sua classe entre propriedades que devem acionar a recomposição e outras propriedades que não exigem isso, pois elas também seriam alteradas quando a recomposição fosse acionada.
Para habilitar o salto forte no conjunto de arquivos Gradle
Portanto, as alterações acima seriam aplicadas às classes, lambdas e funções
Cuidado com as sugestões feitas nos comentários. Se você mudar
val
paravar
, você quebrará a funcionalidade do seu aplicativo, pois o Compose não pode observar mudanças em variáveis mutáveis.Como resultado, quando você digitar, o Jetpack Compose não acionará mais uma recomposição, e você não verá nada aparecendo no seu arquivo
TextField
.No Jetpack Compose, eu consideraria absolutamente comum armazenar todos os valores de um formulário dentro de um único
data class
. Você não deve se preocupar com problemas de desempenho aqui até que realmente os tenha.