Gerenciar alterações de configuração

Algumas configurações do dispositivo podem mudar enquanto o app está em execução. Essas configurações incluem, entre outras:

  • Tamanho de exibição do app
  • Orientação da tela
  • Tamanho e peso da fonte
  • Localidade
  • Modo escuro x modo claro
  • Disponibilidade de teclado

A maioria dessas mudanças de configuração ocorre devido a alguma interação do usuário. Por exemplo, girar ou dobrar o dispositivo muda a quantidade de espaço disponível na tela para o app. Da mesma forma, mudar as configurações do dispositivo, como tamanho da fonte, idioma ou tema preferido, altera esses valores no objeto Configuration.

Esses parâmetros geralmente exigem mudanças grandes na interface do app. Por isso, a plataforma Android tem um mecanismo específico para quando acontecem essas mudanças. Esse mecanismo é a recriação de Activity.

Recriação de atividades

O sistema recria uma Activity quando ocorre uma mudança de configuração. Para fazer isso, o sistema chama onDestroy e destrói a instância de Activity atual. Em seguida, ele cria uma nova instância usando onCreate, e essa nova instância de Activity é inicializada com a configuração atualizada. Isso também significa que o sistema recria a interface com a nova configuração.

Normalmente, o Activity funciona como um host para elementos combináveis. Quando o Activity é recriado, o Compose também recria a interface usando os novos valores de configuração.

O comportamento de recriação ajuda o app a se adaptar a novas configurações, recarregando-o automaticamente com os recursos alternativos que correspondem à nova configuração do dispositivo.

Exemplo de recriação

Considere um elemento combinável que mostra um título estático usando um recurso de string:

// In the res/values/strings.xml file
// <string name="compose">Jetpack Compose</string>

// In your Compose code
Text(
    text = stringResource(R.string.compose)
)

Quando o Activity é criado, o elemento combinável Text lê a configuração atual (como idioma) e resolve o recurso de string apropriado.

Se o idioma mudar, o sistema vai recriar a atividade. Quando isso acontece, o Compose recria a interface. Como stringResource lê a configuração atual, o título é atualizado automaticamente para o valor localizado correto.

A recriação também limpa todos os estados mantidos como campos na Activity.

Para preservar o estado da interface em todas as mudanças de configuração, use padrões recomendados de gerenciamento de estado. Use ViewModel para dados e lógica de negócios e rememberSaveable para estado no nível da interface. Com esses mecanismos, seu estado sobrevive à recriação de Activity enquanto a interface é atualizada para refletir a nova configuração.

Para mais informações sobre como salvar o estado no Compose, consulte Salvar o estado da interface no Compose.

Expectativas dos usuários

O usuário de um app espera que o estado seja preservado. Se um usuário estiver preenchendo um formulário e abrir outro app no modo de várias janelas para procurar alguma informação, ele terá uma experiência ruim se retornar a um formulário em branco ou para outra parte do app. Como desenvolvedor, você precisa fornecer uma experiência de usuário consistente, com mudanças de configuração e recriação de atividades.

Para verificar se o estado é preservado no aplicativo, é possível executar ações que causem mudanças de configuração enquanto o app está em primeiro plano e em segundo plano. Essas ações incluem:

  • Girar o dispositivo
  • Entrar no modo de várias janelas
  • Redimensionar o aplicativo no modo de várias janelas ou em uma janela de formato livre
  • Dobrar um dispositivo dobrável com várias telas
  • Mudar o tema do sistema, como o modo escuro ou o modo claro
  • Mudar o tamanho da fonte
  • Mudar o idioma do sistema ou do app
  • Conectar ou desconectar um teclado de hardware
  • Conectar ou desconectar uma base

Há várias abordagens que podem ser adotadas para preservar o estado relevante durante a recriação da Activity. Qual será usado depende do tipo de estado que você quer preservar:

  • Persistência local para lidar com o encerramento do processo para dados complexos ou grandes. O armazenamento local persistente inclui bancos de dados ou o DataStore.
  • Objetos retidos, como instâncias de ViewModel para processar o estado relacionado à interface na memória enquanto o usuário está usando o app.
  • rememberSaveable para preservar o estado transitório da interface em todas as mudanças de configuração e encerramento do processo iniciado pelo sistema. Isso é adequado para estados que dependem da entrada do usuário, da posição de rolagem ou da navegação, mas não pertencem a um ViewModel.

Para saber mais sobre as APIs das abordagens e quando o uso de cada uma delas é adequado, consulte Salvar estados da interface.

Restringir a recriação de atividades

É possível impedir a recriação automática de atividades para determinadas mudanças de configuração. Em apps modernos somente com Compose, a interface é recomposta de qualquer maneira, mas é recomendado processar a mudança de configuração diretamente.

Por padrão, uma mudança de configuração força o sistema a destruir e recriar a atividade, incluindo a interface e todos os objetos derivados dela. Se você declarar que a atividade processa a mudança de configuração, o sistema vai impedir isso. Em vez disso, apenas o objeto Configuration é atualizado, e o Compose recompõe a UI com os novos valores.

Processar mudanças de configuração diretamente no Compose tem vários benefícios:

  • Melhor desempenho:recompor a interface é menos caro do que um ciclo completo de recriação de Activity, principalmente para mudanças pequenas.
  • Animações fluidas:evitar a reinicialização de uma atividade permite executar animações contínuas em mudanças de configuração, como transições de layout suaves durante a rotação do dispositivo.
  • Preservação de estado:manter a instância da atividade reduz o risco de perda transitória do estado da interface durante um evento como a rotação da tela. É importante lembrar que você ainda precisa processar a preservação de estado para o encerramento do processo iniciado pelo sistema.

Se quiser desativar a recriação de atividades para mudanças específicas de configuração, adicione o tipo de configuração a android:configChanges na entrada <activity> no arquivo AndroidManifest.xml. Os valores possíveis aparecem na documentação do atributo android:configChanges.

O código do manifesto abaixo desativa a recriação da Activity para MyActivity quando a orientação da tela e a disponibilidade do teclado mudam:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

Reagir a mudanças de configuração

O Jetpack Compose permite que seu app reaja com mais facilidade a mudanças de configuração. No entanto, se você desativar a recriação da Activity para todas as mudanças de configuração sempre que possível, o app ainda precisará processar as mudanças corretamente.

O objeto Configuration está disponível na hierarquia de interfaces do Compose com o local de composição LocalConfiguration. Sempre que ele muda, as funções combináveis que leem LocalConfiguration.current são recompostas. Para saber mais sobre como os locais de composição funcionam, consulte Dados com escopo local usando o CompositionLocal.

Exemplo

No exemplo abaixo, um elemento combinável mostra uma data com um formato específico. Esse elemento reage às mudanças de configuração de localidade do sistema chamando ConfigurationCompat.getLocales com LocalConfiguration.current.

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

Para evitar a recriação da Activity quando a localidade muda, a Activity que hospeda o código do Compose precisa desativar as mudanças de configuração de localidade. Para isso, defina android:configChanges como locale|layoutDirection.

Mudanças de configuração: principais conceitos e práticas recomendadas

Estes são os principais conceitos que você precisa conhecer ao trabalhar em mudanças de configuração:

  • Configurações:as configurações do dispositivo definem como a interface será mostrada para o usuário, como tamanho de exibição do app, localidade ou tema do sistema. No Compose, você pode acessar valores de configuração usando LocalConfiguration.
  • Mudanças de configuração:as configurações mudam com a interação do usuário. Por exemplo, o usuário pode mudar as configurações do dispositivo ou interagir fisicamente com ele. Não há como evitar mudanças de configuração.
  • Recriação da Activity:as mudanças de configuração resultam na recriação da Activity por padrão. Esse é um mecanismo integrado para reinicializar o estado do app na nova configuração.
  • Destruição da Activity:a recriação da Activity faz com que o sistema destrua a instância antiga da Activity e crie outra no lugar. A instância antiga está obsoleta. Evite manter referências a objetos com escopo de ciclo de vida além do escopo pretendido.
  • Estado:o estado na instância antiga da Activity não está presente na nova instância da Activity, porque essas são duas instâncias diferentes de objeto. Em vez de vincular o estado à atividade, use as APIs recomendadas para preservar o estado do app e do usuário, conforme descrito em Salvar estados da interface.
  • Desativação:desativar a recriação de atividade para um tipo de mudança de configuração exige que o app seja atualizado corretamente em reação à nova configuração. Para a maioria dos apps do Compose, isso não é recomendado.

Para oferecer uma boa experiência ao usuário, siga estas práticas recomendadas:

  • Prepare-se para mudanças frequentes na configuração:não presuma que mudanças de configuração são raras ou nunca acontecem, independente do nível da API, do formato ou do kit de ferramentas da interface. Quando um usuário causa uma mudança na configuração, ele espera que os apps sejam atualizados e continuem funcionando corretamente com a nova configuração.
  • Preserve o estado:não perca o estado do usuário quando ocorre a recriação da Activity. Siga as instruções descritas em Salvar estados da interface usando APIs como ViewModel e rememberSaveable.
  • Evite a desativação como uma correção rápida:não desative a recriação da Activity como um atalho para evitar a perda de estado. Se quiser desativar a recriação de atividade, é necessário cumprir a promessa de processar a mudança. Você ainda pode perder o estado devido à recriação da Activity por outras mudanças de configuração, ao encerramento do processo ou ao fechamento do app. É impossível desativar completamente a recriação da Activity. Siga as instruções descritas em Salvar estados da interface para preservar o estado.
  • Não evite mudanças de configuração:não coloque restrições de orientação, proporção ou redimensionamento para evitar mudanças na configuração e recriação de Activity. Isso afeta negativamente os usuários que querem usar seu app da maneira que preferem.

Gerenciar mudanças de configuração de tamanho

Mudanças na configuração com base no tamanho podem acontecer a qualquer momento e são mais prováveis quando o app é executado em um dispositivo com tela grande em que os usuários podem entrar no modo de várias janelas. Eles esperam que o app funcione bem nesse ambiente.

Existem dois tipos gerais de mudanças de tamanho: significativa e insignificante. Uma mudança significativa é aquela em que um conjunto diferente de recursos alternativos se aplica à nova configuração devido a uma diferença no tamanho da tela, como largura, altura ou menor largura. Esses recursos incluem aqueles que o próprio app define e os de qualquer uma das bibliotecas dele.

Restringir a recriação de atividades para mudanças de configuração baseadas no tamanho

Quando você desativa a recriação da Activity para mudanças de configuração com base no tamanho, o sistema não recria a Activity. Em vez disso, ele recebe uma chamada para Activity.onConfigurationChanged. Todos os elementos combináveis que leem LocalConfiguration.current são recompostos automaticamente para refletir o novo tamanho.

A recriação da Activity é desativada para mudanças de configuração com base no tamanho quando há android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" no arquivo de manifesto.

Outros recursos

Para mais informações sobre como lidar com mudanças de configuração, consulte os recursos adicionais a seguir:

Documentação

Visualiza conteúdo