AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79041850
Accepted
Ali Asjad
Ali Asjad
Asked: 2024-10-01 14:10:42 +0800 CST2024-10-01 14:10:42 +0800 CST 2024-10-01 14:10:42 +0800 CST

Existe uma maneira mais eficiente de atualizar propriedades de classe de dados no Jetpack Compose Kotlin?

  • 772

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:

  1. 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?
  2. 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?

  • 2 2 respostas
  • 75 Views

2 respostas

  • Voted
  1. Best Answer
    2024-10-01T17:25:10+08:002024-10-01T17:25:10+08:00

    Com Views, foi recomendado não instanciar novos objetos, especialmente em onDrawfunçõ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

    insira a descrição da imagem aqui

    insira a descrição da imagem aqui

    @Composable
    fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
       var selected by remember { mutableStateOf(false) }
    
       Row(modifier) {
          ContactDetails(contact)
          ToggleButton(selected, onToggled = { selected = !selected })
       }
    }
    

    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.

    class Car(
        val id: Int = 0,
        val brand: String = "",
        var model: String = "",
        val year: Int = 2020,
    )
    
    
    data class MyListClass(val currentCar: Car, val items: List<Car>)
    
        @Preview
        @Composable
        fun TestCopy() {
            var myListClass by remember {
                mutableStateOf(MyListClass(
                    currentCar = Car(),
                    items = List(10) {
                        Car()
                    }
                ))
            }
            Button(
                onClick = {
                    val temp = myListClass
        
                    myListClass = myListClass.copy(
                        currentCar = Car(id = 1)
                    )
        
                    println("temp===myListClass ${temp === myListClass}\n" +
                            "temp car===zmyListClass car ${temp.currentCar === myListClass.currentCar}\n" +
                            "temp list===myListClass ${temp.items === myListClass.items}")
                }
            ) {
                Text("Copy...")
            }
        }
    

    Impressões

     I  temp===myListClass false
     I  temp car===zmyListClass car false
     I  tem list===myListClass true
    

    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 Stateou 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 novo valueque você definiu é ==para o anterior. Em classes de dados, equals é determinado por parâmetros do construtor primário.

    @Suppress("UNCHECKED_CAST")
    fun <T> structuralEqualityPolicy(): SnapshotMutationPolicy<T> =
        StructuralEqualityPolicy as SnapshotMutationPolicy<T>
    
    private object StructuralEqualityPolicy : SnapshotMutationPolicy<Any?> {
        override fun equivalent(a: Any?, b: Any?) = a == b
    
        override fun toString() = "StructuralEqualityPolicy"
    }
    

    Ao alterar esta política, você pode acionar a recomposição mesmo com o mesmo objeto, como

    @Stable
    data class Car(
        val id: Int = 0,
        val brand: String = "",
        var model: String = "",
        val year: Int = 2020,
    )
    
    @Preview
    @Composable
    fun CarScreen() {
        var car by remember {
            mutableStateOf(
                value = Car(),
                policy = neverEqualPolicy()
            )
        }
    
        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()`
    
                    car = car.apply {
                        model = newModel
                    }
                },
                label = { Text("Model") }
            )
    
            Text("Car brand: ${car.brand}, model: ${car.model}")
        }
    }
    

    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

    @Stable
    class CarUiState(
        brand: String = "",
        model: String = "",
    ) {
    
        var id: Int = 0
        var year: Int = 2020
    
        var brand by mutableStateOf(brand)
        var model by mutableStateOf(model)
    }
    
    @Preview
    @Composable
    fun CarScreen() {
    
        val carUiState = remember {
            CarUiState()
        }
        Column {
            TextField(
                value = carUiState.brand,
                onValueChange = { newBrand ->
                    carUiState.brand = newBrand
                },
                label = { Text("Brand") }
            )
    
            TextField(
                value = carUiState.model,
                onValueChange = { newModel ->
                    carUiState.model = newModel
                },
                label = { Text("Model") }
            )
    
            Text("Car brand: ${carUiState.brand}, model: ${carUiState.model}, year: ${carUiState.year}")
        }
    }
    

    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

    composeCompiler {
        // Configure compose compiler options if required
        enableStrongSkippingMode = true
    }
    

    Portanto, as alterações acima seriam aplicadas às classes, lambdas e funções

    Composables com parâmetros instáveis ​​podem ser ignorados.

    Parâmetros instáveis ​​são comparados para igualdade por meio de igualdade de instância (===)

    Parâmetros estáveis ​​continuam a ser comparados para igualdade com Object.equals()

    Todos os lambdas em funções composable são automaticamente lembrados. Isso significa que você não precisará mais envolver lambdas em remember para garantir que um composable que use um lambda, pule.

    • 6
  2. BenjyTec
    2024-10-01T17:34:21+08:002024-10-01T17:34:21+08:00

    Cuidado com as sugestões feitas nos comentários. Se você mudar valpara var, você quebrará a funcionalidade do seu aplicativo, pois o Compose não pode observar mudanças em variáveis ​​mutáveis.

    data class Car(
        val id: Int = 0, var brand: String = "", val model: String = "", val year: Int = 2020
    )
    
    onValueChange = { newBrand ->
        car.brand = newBrand   // This will not work!
    }
    

    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.

    • 3

relate perguntas

  • Adicionar número de série para atividade de cópia ao blob

  • A fonte dinâmica do empacotador duplica artefatos

  • Selecione linhas por grupo com 1s consecutivos

  • Lista de chamada de API de gráfico subscritoSkus estados Privilégios insuficientes enquanto os privilégios são concedidos

  • Função para criar DFs separados com base no valor da coluna

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve