Gerenciar mudanças de configuração

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Algumas configurações de dispositivo podem mudar durante a execução, como a orientação da tela, a disponibilidade do teclado e a ativação do modo de várias janelas. Quando isso acontece, o Android reinicia a Activity em execução (onDestroy() é chamado, seguido por onCreate()). O comportamento de reinicialização foi criado para ajudar o aplicativo a se adaptar a novas configurações, recarregando-o automaticamente com recursos alternativos que correspondam à configuração do novo dispositivo.

Para gerenciar corretamente uma reinicialização, é importante que sua atividade restaure o estado anterior. Você pode usar uma combinação de objetos onSaveInstanceState(), ViewModel e o armazenamento persistente para salvar e restaurar o estado da IU da sua atividade durante as mudanças de configuração. Para saber mais sobre como salvar o estado da sua atividade, leia Como salvar estados da IU.

Para testar se o aplicativo reinicia com o estado intacto, invoque as mudanças de configuração (como a orientação da tela) enquanto executa mais tarefas. O aplicativo precisa ser capaz de reiniciar a qualquer momento sem perda de dados ou estados do usuário para processar eventos como mudanças de configuração ou quando o usuário recebe uma chamada telefônica e retorna depois da exclusão do processo. Para conferir como restaurar o estado da atividade, leia sobre o Ciclo de vida da atividade.

No entanto, talvez você se depare com uma situação em que o reinício do aplicativo e a restauração representem quantidades significativas de dados que podem ser custosos e prejudicar a experiência do usuário. Nessa situação, há duas opções:

  1. Reter um objeto durante uma mudança de configuração

    Permita que a atividade reinicie quando uma configuração mudar, mas transporte um objeto com estado para a nova instância da atividade.

  2. Processar a mudança de configuração por conta própria

    Não é recomendado manipular as mudanças de configuração devido à complexidade desse processo. No entanto, se você não conseguir preservar o estado da IU usando as opções preferidas (objetos onSaveInstanceState(), ViewModel e armazenamento permanente), é possível impedir que o sistema a reinicie durante determinadas mudanças de configuração. Seu app vai receber um callback quando as configurações mudarem para que você possa atualizar manualmente sua atividade conforme necessário.

Reter um objeto durante uma mudança de configuração

Se a retenção da atividade exigir que você extraia grandes conjuntos de dados, restabeleça uma conexão de rede ou execute outras operações intensivas, um reinício completo devido a uma mudança de configuração pode prejudicar a experiência do usuário. Além disso, pode não ser possível restaurar completamente o estado da atividade com o Bundle que o sistema salva com o callback onSaveInstanceState(). Ele não foi criado para transportar objetos grandes (como bitmaps), e os dados contidos nele precisam ser serializados e desserializados na linha de execução principal, o que pode consumir muita memória e retardar a mudança de configuração. Nessa situação, você pode aliviar o peso de reinicializar parte da sua atividade usando um ViewModel. Os objetos ViewModel são preservados em todas as mudanças de configuração. Por isso, eles são o local perfeito para manter seus dados de IU sem precisar consultá-los novamente. Para saber mais sobre o uso da classe ViewModel nos seus apps, leia a Visão geral do ViewModel.

Processar a mudança de configuração por conta própria

Se o aplicativo não tiver que atualizar recursos durante uma mudança de configuração específica e se houver alguma limitação de performance que impeça a atividade de reiniciar, vai ser possível declarar que a atividade processa a mudança de configuração, o que vai evitar que o sistema reinicie a atividade.

Cuidado: processar a mudança de configuração por conta própria pode dificultar muito o uso de recursos alternativos, já que o sistema não os aplicará automaticamente. Essa técnica precisa ser considerada um último recurso, quando é preciso evitar reinícios devido a uma mudança de configuração, e não é recomendada para a maioria dos aplicativos.

Para declarar que a atividade processa uma mudança de configuração, edite o elemento <activity> apropriado no arquivo de manifesto para que inclua o atributo android:configChanges com um valor que represente a configuração a ser processada. Os valores possíveis estão listados na documentação do atributo android:configChanges. Os valores mais comuns são "orientation", "screenSize" "screenLayout" e "keyboardHidden".

  • O valor "orientation" evita reinicializações quando a orientação da tela é modificada.
  • O valor "screenSize" também impede reinicializações quando a orientação muda, mas apenas para o Android 3.2 (nível 13 da API) e versões mais recentes.
  • O valor "screenLayout" é necessário para detectar mudanças que possam ser acionadas por dispositivos como smartphones dobráveis e Chromebooks conversíveis.
  • O valor "keyboardHidden" impede a reinicialização quando a disponibilidade do teclado é modificada.

Se você quiser processar as mudanças de configuração manualmente no app, declare os valores "orientation", "screenSize" e "screenLayout" nos atributos android:configChanges. Para declarar vários valores de configuração no atributo, use um separador, como o caractere de barra reta |.

Por exemplo, o código de manifesto abaixo declara uma atividade que processa tanto a mudança de orientação da tela quanto a disponibilidade do teclado:

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

Agora, quando ocorre uma mudança em uma dessas configurações, o MyActivity não é reiniciado. Em vez disso, MyActivity recebe uma chamada para onConfigurationChanged().

O método onConfigurationChanged() é transmitido ao objeto Configuration, que especifica a nova configuração do dispositivo. Ao ler os campos no objeto Configuration, é possível determinar a nova configuração e atualizar os recursos na interface para fazer as mudanças adequadas. No momento em que o método é chamado, o objeto Resources da atividade é atualizado para retornar recursos com base na nova configuração, o que facilita a redefinição de elementos da IU sem que o sistema reinicie a atividade.

Por exemplo, a implementação de onConfigurationChanged() abaixo verifica a orientação atual do dispositivo:

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks the orientation of the screen
    if (newConfig.orientation === Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show()
    } else if (newConfig.orientation === Configuration.ORIENTATION_PORTRAIT) {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show()
    }
}

Java

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

O objeto Configuration representa todas as configurações atuais, não somente as que mudaram. Na maior parte do tempo, não importa como a configuração foi modificada. Basta reatribuir todos os recursos que apresentam alternativas à configuração que está sendo processada. Por exemplo, como o objeto Resources está atualizado, você pode redefinir qualquer instância ImageView com setImageResource() e o recurso adequado para a nova configuração vai ser usado (como descrito na Visão geral de recursos de app).

Os valores dos campos de Configuration são números inteiros que correspondem a constantes específicas da classe Configuration. Para conferir a documentação sobre as constantes que for usar em cada campo, consulte o campo em questão na referência sobre Configuration.

Não esqueça: ao declarar a atividade para processar uma mudança de configuração, você é responsável por redefinir todos os elementos que fornecem alternativas. Se você declarar a atividade para processar a mudança de orientação e tiver imagens que mudariam entre as orientações de paisagem e retrato, vai ser necessário reatribuir cada recurso a cada elemento durante onConfigurationChanged().

Se não for necessário atualizar o aplicativo com base nessas mudanças de configuração, não implemente onConfigurationChanged(). Nesse caso, todos os recursos usados antes da mudança de configuração ainda são usados, e somente o reinício da atividade é evitado.

No entanto, não considere essa técnica como um fuga da retenção de estado durante o ciclo de vida normal da atividade. O aplicativo precisa sempre conseguir ser desligado e reiniciado com o estado anterior intacto. Mudanças de configuração que não podem ser impedidas podem reiniciar o aplicativo. Se o usuário sair do aplicativo e o app for colocado em segundo plano, o sistema vai poder destruir o aplicativo.