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

Cómo manejar cambios en la configuración

Algunas configuraciones de dispositivos pueden cambiar durante el tiempo de ejecución (por ejemplo, la orientación de la pantalla, la disponibilidad del teclado y el modo de ventanas múltiples). Cuando se producen estos cambios, Android reinicia la Activity en ejecución (se llama a onDestroy() y luego a onCreate()). El comportamiento de reinicio está diseñado para ayudar a tu aplicación a adaptarse a las configuraciones nuevas recargándola automáticamente con recursos alternativos que coinciden con la nueva configuración del dispositivo.

Para manejar correctamente un reinicio, es importante que tu actividad recupere su estado anterior. Puedes usar una combinación de onSaveInstanceState(), objetos de ViewModel y almacenamiento persistente para guardar y restablecer el estado de IU de tu actividad en los cambios de configuración. Para obtener más información sobre cómo guardar el estado de la actividad, lee Cómo guardar estados de IU.

Para comprobar si tu aplicación se reinicia con su estado intacto, debes invocar cambios de configuración (por ejemplo, cambiar la orientación de la pantalla) mientras realizas diversas tareas en la aplicación. Tu aplicación debe ser capaz de reiniciarse en cualquier momento sin perder datos del usuario o el estado para poder manejar eventos tales como cambios de configuración o casos en los que el usuario recibe una llamada telefónica entrante y luego regresa a tu aplicación mucho más tarde, cuando probablemente ya se ha destruido el proceso de la aplicación. Para aprender a restaurar el estado de tu actividad, lee acerca del ciclo de vida de la actividad.

Sin embargo, quizás exista una situación en la que reiniciar tu aplicación y restaurar grandes cantidades de datos pueda ser costoso y crear una experiencia deficiente para el usuario. En ese caso, tienes otras dos opciones:

  1. Retener un objeto durante un cambio de configuración

    Permite que tu actividad se reinicie cuando cambia una configuración, pero lleva un objeto con estado a la nueva instancia de tu actividad.

  2. Manejar el cambio de configuración tú mismo

    No se recomienda que manejes los cambios de configuración por tu cuenta debido a la complejidad oculta de esta acción. Sin embargo, si no puedes conservar el estado de tu IU utilizando las opciones preferidas (onSaveInstanceState(), ViewModels y almacenamiento persistente), puedes evitar que el sistema reinicie tu actividad durante ciertos cambios de configuración. Tu aplicación recibirá una devolución de llamada cuando cambien las configuraciones para que puedas actualizar manualmente tu actividad según sea necesario.

Cómo retener un objeto durante un cambio de configuración

Si para reiniciar tu actividad es necesario recuperar grandes conjuntos de datos, restablecer una conexión de red o realizar otras operaciones intensivas, un reinicio completo debido a un cambio de configuración podría generar una experiencia lenta para el usuario. Además, quizás no puedas restaurar completamente el estado de la actividad con el Bundle que el sistema guarda con la devolución de llamada onSaveInstanceState(), ya que no se diseñó para transferir objetos grandes (como mapas de bits), y los datos que contiene deben serializarse y, luego, deserializarse, lo cual puede consumir mucha memoria y hacer que el cambio de configuración sea lento. En tal situación, puedes aliviar la carga de reiniciar parte de tu actividad utilizando un objeto ViewModel. Los ViewModels se conservan en todos los cambios de configuración, por lo que son el lugar perfecto para guardar los datos de la interfaz de usuario sin tener que consultarlos de nuevo. Para obtener más información sobre el uso de ViewModels en tus aplicaciones, lee la guía de ViewModel.

Maneja el cambio de configuración tú mismo

Si tu aplicación no necesita actualizar recursos durante un cambio de configuración específico y tienes una limitación de rendimiento que te exige que evites el reinicio de la actividad, puedes declarar que tu actividad maneja el cambio de configuración por sí misma, lo cual evita que el sistema reinicie la actividad.

Advertencia: Al manejar el cambio de configuración tú mismo, puede resultar mucho más difícil utilizar recursos alternativos, ya que el sistema no los aplica automáticamente. Esta técnica debe considerarse como último recurso cuando debes evitar los reinicios debido a un cambio de configuración, y no se recomienda para la mayoría de las aplicaciones.

Para declarar que tu actividad maneja un cambio de configuración, edita el elemento <activity> correspondiente en el archivo de manifiesto para incluir el atributo android:configChanges con un valor que represente la configuración que deseas manejar. Los valores posibles se enumeran en la documentación del atributo android:configChanges. Los valores comunes más usados son "orientation", "screenSize" y "keyboardHidden". El valor "orientation" evita reinicios cuando cambia la orientación de la pantalla. El valor "screenSize" también evita reinicios cuando cambia la orientación, pero a partir de Android 3.2 (nivel de API 13). Si deseas gestionar manualmente los cambios de configuración en tu aplicación, debes declarar los valores "orientation" y "screenSize" en los atributos android:configChanges. El valor "keyboardHidden" evita reinicios cuando cambia la disponibilidad del teclado. Puedes declarar varios valores de configuración en el atributo separándolos con una barra vertical |.

Por ejemplo, el código de manifiesto siguiente declara una actividad que maneja tanto el cambio de orientación de la pantalla como el cambio de disponibilidad del teclado:

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

Cuando cambia una de estas configuraciones, MyActivity no se reinicia. En cambio, MyActivity recibe una llamada a onConfigurationChanged(). A este método se le pasa un objeto Configuration que especifica la nueva configuración del dispositivo. Al leer los campos de Configuration, puedes determinar la nueva configuración y realizar los cambios correspondientes actualizando los recursos utilizados en tu interfaz. Cuando se llama a este método, el objeto Resources de tu actividad se actualiza para mostrar recursos basados en la nueva configuración, de modo que puedas restablecer fácilmente elementos de tu IU sin que el sistema reinicie la actividad.

Por ejemplo, en la siguiente implementación onConfigurationChanged() comprueba la orientación actual del 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();
    }
}

El objeto Configuration representa todas las configuraciones actuales, no solo las que han cambiado. La mayoría de las veces, no te importará la forma exacta en la que ha cambiado la configuración y podrás simplemente reasignar todos los recursos que proporcionan alternativas a la configuración que estás manejando. Por ejemplo, dado que el objeto Resources ahora está actualizado, puedes restablecer cualquier ImageView con setImageResource(), y se usa el recurso correspondiente para la nueva configuración (como se describe en Cómo proveer recursos).

Ten en cuenta que los valores de los campos Configuration son enteros y coinciden con constantes específicas de la clase Configuration. Para hallar documentación sobre las constantes que deben usarse con cada campo, consulta el campo correspondiente en la referencia de Configuration.

Recuerda que, cuando declaras que tu actividad maneja un cambio de configuración, eres responsable de restablecer los elementos para los cuales proporcionas alternativas. Si declaras que tu actividad maneja el cambio de orientación y tiene imágenes que deben pasar de la orientación horizontal a la vertical, debes reasignar cada recurso a cada elemento durante onConfigurationChanged().

Si no necesitas actualizar tu aplicación según estos cambios de configuración, como alternativa, puedes optar por no implementar onConfigurationChanged(). En este caso, todos los recursos utilizados antes del cambio de configuración se siguen utilizando, y tú solamente evitaste el reinicio de tu actividad. Sin embargo, tu aplicación siempre debe ser capaz de cerrarse y reiniciarse con su estado anterior intacto, por lo que no debes considerar esta técnica como un escape de la retención de tu estado durante el ciclo de vida normal de la actividad. No solo porque existen otros cambios de configuración que no puedes evitar que reinicien tu aplicación, sino también porque debes manejar ciertos eventos, por ejemplo, cuando el usuario abandona tu aplicación y esta finaliza antes de que el usuario vuelva a ella.