Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Gerenciar alterações de configuração

Algumas configurações de dispositivo podem mudar durante o tempo de execução, como orientação da tela, disponibilidade do teclado e ativação do modo de várias janelas. Quando ocorre uma alteração, o Android precisa reiniciar a execução de Activity (onDestroy() é chamado, seguido de onCreate()). O comportamento de reinício foi projetado para ajudar o aplicativo a se adaptar a novas configurações atualizando automaticamente o aplicativo com recursos alternativos que correspondam à configuração do 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 interface do usuário da sua atividade nas alterações de configuração. Para saber mais sobre como salvar o estado da sua Activity, leia Como salvar estados da IU.

Para testar se o aplicativo reinicia com o estado intacto, invoque as alterações de configuração (como alterações na orientação da tela) enquanto executa diversas tarefas no aplicativo. O aplicativo deve ser capaz de reiniciar a qualquer momento sem perda de dados ou estados do usuário para lidar com eventos como alterações de configuração ou quando o usuário recebe uma chamada telefônica e retorna ao seu aplicativo depois da exclusão do processo. Para ver 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 alteração de configuração

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

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

    Não é recomendado manipular as alterações de configuração devido à complexidade desse processo. No entanto, se você não conseguir preservar o estado da sua IU usando as opções preferidas (onSaveInstanceState (), ViewModels e armazenamento persistente), poderá impedir que o sistema a reinicie durante determinadas alterações de configuração. Seu aplicativo receberá um callback quando as configurações forem alteradas para que você possa atualizar manualmente sua atividade conforme necessário.

Retenção de um objeto durante uma alteração de configuração

Se a retenção da atividade exigir a recuperação de grandes conjuntos de dados, restabelecer uma conexão de rede ou executar outras operações intensivas, um reinício completo devido a uma alteração 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 projetado para transportar objetos grandes (como bitmaps) e os dados contidos devem ser serializados e, em seguida, desserializados no thread principal, o que pode consumir muita memória e retardar a alteração de configuração. Nessa situação, você pode aliviar o fardo de reinicializar parte da sua atividade usando um objeto ViewModel. Os ViewModels são preservados em todas as alterações de configuração. Por isso, eles são o local perfeito para manter seus dados de IU sem precisar consultá-los novamente. Para mais informações sobre como usar os ViewModels nos seus aplicativos, leia o guia de ViewModel.

Processamento da alteração de configuração por conta própria

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

Atenção: processar a alteração 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 deve ser considerada um último recurso, quando é preciso evitar reinícios devido a uma alteração de configuração, e não é recomendada para a maioria dos aplicativos.

Para declarar que a atividade processa uma alteração 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" e "keyboardHidden". O valor "orientation" evita reinicializações quando a orientação da tela é alterada. O valor "screenSize" também impede a reinicialização quando a orientação é alterada, mas começa no Android 3.2 (API de nível 13). Se você quiser manipular manualmente as alterações de configuração no seu aplicativo, você deve declarar os dois valores "orientation" e "screenSize" nos atributos android:configChanges. O valor "keyboardHidden" impede a reinicialização quando a disponibilidade do teclado é alterada. Para declarar vários valores de configuração no atributo, use um separador na forma do caractere barra reta |.

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

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

Agora, quando uma dessas configurações é alterada, MyActivity não é reiniciada. Em vez disso, MyActivity recebe uma chamada para onConfigurationChanged(). Um objeto Configuration é passado a esse método e especifica a nova configuração do dispositivo. Ao ler os campos em Configuration, é possível determinar a nova configuração e atualizar os recursos na interface para fazer as alterações 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() a seguir 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 foram alteradas. Na maior parte do tempo, não importa como a configuração foi alterada. 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 ImageView com setImageResource() e será usado o recurso apropriado para a nova configuração (como descrito em Como fornecer recursos).

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

Lembre-se: ao declarar a atividade para processar uma alteração de configuração, você é responsável por redefinir todos os elementos que fornecem alternativas. Se você declarar a atividade para processar a alteração de orientação e tiver imagens que alterariam entre paisagem e retrato, é preciso reatribuir cada recurso a cada elemento durante onConfigurationChanged().

Se não for necessário atualizar o aplicativo com base nessas alterações de configuração, não implemente onConfigurationChanged(). Nesse caso, todos os recursos usados antes da alteração de configuração ainda são usados, e somente o reinício da atividade é evitado. No entanto, o aplicativo deve sempre ser capaz de encerrar e reiniciar com o estado anterior intacto. Portanto, essa técnica não deve ser considerada uma fuga da retenção do estado durante o ciclo de vida normal da atividade. Não somente porque há outras alterações de configuração impossíveis de evitar que reiniciem o aplicativo, mas também porque é preciso processar eventos como o do usuário que sai do aplicativo e o processo é destruído antes de um novo acesso.